Token过期后怎么自动刷新还不让用户感知?

Zz爱红 阅读 143

我在用 JWT 做用户认证,前端每次请求都带 Authorization: Bearer xxx。但 token 一小时就过期,过期后接口返回 401,用户就得重新登录,体验太差了。

我看别人说可以用 refresh token 自动续期,但我试了下,在 axios 的 response interceptor 里拦截 401 后去请求新 token,结果发现如果多个请求同时触发 401,会发好几次刷新请求,还可能拿不到最新的 access token。有没有靠谱的处理方式?

axios.interceptors.response.use(
  res => res,
  async err => {
    if (err.response?.status === 401) {
      const newToken = await refreshToken(); // 这里并发会出问题
      localStorage.setItem('token', newToken);
      // 重试原请求...
    }
  }
);
我来解答 赞 5 收藏
二维码
手机扫码查看
1 条解答
一柯言
一柯言 Lv1
这个问题很常见,核心就是加个锁来控制并发。

简单说,用一个 isRefreshing 标记位来控制:

let isRefreshing = false;
// 用一个队列存正在等待的请求
let requests = [];

axios.interceptors.response.use(
res => res,
async err => { const originalConfig = err.config;

// 如果是 401 且不是刷新 token 的请求
if (err.response?.status === 401 && !originalConfig._retry) {
// 如果已经在刷新了,把当前请求放进队列等一等
if (isRefreshing) {
return new Promise(resolve => {
requests.push(() => {
resolve(axios(originalConfig));
});
});
}

originalConfig._retry = true;
isRefreshing = true;

try {
const newToken = await refreshToken();
localStorage.setItem('token', newToken);

// 刷新成功后,把队列里的请求都重发一遍
requests.forEach(cb => cb());
requests = [];

// 重发当前失败的请求
return axios(originalConfig);
} catch (error) {
// 刷新也失败了,清空队列,直接跳转登录
requests.forEach(cb => cb());
requests = [];
window.location.href = '/login';
return Promise.reject(error);
} finally {
isRefreshing = false;
}
}

return Promise.reject(err);
}
);


关键点就两个:

一个是 isRefreshing 标志位,保证同一时间只有一个刷新请求在跑。

另一个是 requests 队列,刷新期间发过来的请求先存着,等新 token 拿到后统一重发。

还有个小细节,refreshToken 这个接口本身要用不同的方式处理,别把自己也拦截成 401 了搞出死循环——比如用不带拦截器的 axios 实例去调刷新接口,或者在拦截器里判断 url。

这样多个请求同时 401 只会触发一次刷新,用户完全无感知,体验就和正常请求一样。
点赞
2026-03-18 08:03