You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Expo中2分钟过期Token的最优刷新方案咨询

Token刷新优化方案

你当前用固定时长的setTimeout刷新Token确实存在不少问题:比如Token实际有效期可能和你设定的2分钟不符、网络波动时刷新请求失败会导致后续请求报错、多次触发useEffect可能创建多个定时器冲突。下面是几种更可靠的优化方案:

方案一:基于Token过期时间动态计算刷新时机

大部分JWT Token里会包含exp字段(过期时间戳),我们可以解析这个字段,在Token即将过期前(比如提前30秒)触发刷新,比固定时间更精准。

实现步骤:

  • 写一个解析Token获取过期时间的函数
  • 在Token更新后,计算剩余有效期,设置对应的定时器
  • 清理旧定时器,避免内存泄漏

示例代码:

// 解析JWT Token,获取过期时间
const getTokenExpiration = (token) => {
  if (!token) return 0;
  const payload = JSON.parse(atob(token.split('.')[1]));
  return payload.exp * 1000; // 转成毫秒
};

React.useEffect(() => {
  let refreshTimer;
  if (token) {
    const expTime = getTokenExpiration(token);
    // 提前30秒刷新
    const refreshDelay = expTime - Date.now() - 30000;
    if (refreshDelay > 0) {
      refreshTimer = setTimeout(() => {
        refreshCurrentToken();
      }, refreshDelay);
    }
  }

  // 组件卸载或token变化时清理定时器
  return () => clearTimeout(refreshTimer);
}, [token]);

// 刷新函数去掉定时器参数,简化逻辑
async function refreshCurrentToken() {
  try {
    const res = await axios.post("https://login.iee.ihu.gr/token", {
      grant_type: "refresh_token",
      code: refresh,
      client_id: cID,
      client_secret: cSecret,
    });
    setToken(res.data.access_token);
    setRefresh(res.data.refresh_token);
  } catch (err) {
    console.log(err);
    // 刷新失败可跳转登录或其他降级逻辑
    // navigate('/login');
  }
}

方案二:请求拦截器触发刷新(推荐)

不用定时轮询,而是在每次发起请求前检查Token状态,即将过期或已过期时先刷新Token,再继续请求。这种方式更高效,避免无意义的定时请求。

实现步骤:

  • 给axios添加请求拦截器
  • 拦截器里检查Token是否即将过期
  • 若需要刷新,先发起刷新请求,更新Token后再继续原请求
  • 加锁防止同时触发多次刷新请求

示例代码:

// 全局刷新锁,防止重复刷新
let isRefreshing = false;
// 存储等待刷新完成后再发起的请求
let requestsQueue = [];

// 创建axios实例
const api = axios.create({
  baseURL: '你的API地址'
});

// 请求拦截器
api.interceptors.request.use(async (config) => {
  const currentToken = token;
  if (!currentToken) {
    // 无Token直接跳转登录
    // navigate('/login');
    return Promise.reject(new Error('无有效Token'));
  }

  const expTime = getTokenExpiration(currentToken);
  // 距离过期不足30秒,触发刷新
  if (expTime - Date.now() < 30000) {
    if (!isRefreshing) {
      isRefreshing = true;
      try {
        const res = await axios.post("https://login.iee.ihu.gr/token", {
          grant_type: "refresh_token",
          code: refresh,
          client_id: cID,
          client_secret: cSecret,
        });
        const newToken = res.data.access_token;
        setToken(newToken);
        setRefresh(res.data.refresh_token);
        // 更新当前请求的Token
        config.headers.Authorization = `Bearer ${newToken}`;
        // 执行队列里的请求
        requestsQueue.forEach(cb => cb(newToken));
        requestsQueue = [];
      } catch (err) {
        console.log(err);
        // 刷新失败跳转登录
        // navigate('/login');
        requestsQueue.forEach(cb => cb(Promise.reject(err)));
        requestsQueue = [];
      } finally {
        isRefreshing = false;
      }
    } else {
      // 正在刷新,把当前请求加入队列
      return new Promise((resolve, reject) => {
        requestsQueue.push((newToken) => {
          if (newToken) {
            config.headers.Authorization = `Bearer ${newToken}`;
            resolve(config);
          } else {
            reject(new Error('Token刷新失败'));
          }
        });
      });
    }
  } else {
    // Token有效,直接设置请求头
    config.headers.Authorization = `Bearer ${currentToken}`;
  }
  return config;
}, (error) => {
  return Promise.reject(error);
});

// 解析Token函数同上
const getTokenExpiration = (token) => {
  if (!token) return 0;
  const payload = JSON.parse(atob(token.split('.')[1]));
  return payload.exp * 1000;
};

方案对比

  • 动态定时刷新:适合需要保持Token持续有效(比如实时推送场景),但仍存在刷新请求失败的风险,需要处理失败后的降级逻辑。
  • 请求拦截器刷新:更高效,只在需要请求时处理,避免无用请求,是大部分业务场景的首选。

另外注意:

  • 刷新Token的接口本身要确保可靠,最好加上重试机制。
  • 刷新失败后要及时引导用户重新登录,避免无限循环刷新。

内容的提问来源于stack exchange,提问作者kostis14

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 20:34:59