Token过期后如何自动刷新并重试请求?

广云 阅读 4

我在用React做登录功能,接口返回401时想自动刷新token再重试原请求,但不知道怎么优雅地处理。试过在拦截器里刷新,但遇到多个请求同时过期时会重复刷新。

现在代码大概是这样:

const api = axios.create();

api.interceptors.response.use(
  res => res,
  async (error) => {
    if (error.response?.status === 401) {
      const newToken = await refreshToken(); // 这里可能被多次调用
      localStorage.setItem('token', newToken);
      return api(error.config); // 重试原请求
    }
    return Promise.reject(error);
  }
);
我来解答 赞 5 收藏
二维码
手机扫码查看
1 条解答
设计师剑博
这个问题很常见,核心痛点就是多个请求同时401时会触发多次刷新。

解决思路很简单:加个标志位把刷新行为锁住,然后用队列把后续请求存起来,等token刷新成功后再逐个重试。

直接上代码:

let isRefreshing = false;
let requestsQueue = []; // 存 pending 的请求

const api = axios.create();

api.interceptors.response.use(
res => res,
async (error) => {
const originalRequest = error.config;

// 判断是否是401且还没重试过
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true; // 标记这个请求已经重试过一次,避免死循环

if (!isRefreshing) {
// 没有正在刷新,开始刷新
isRefreshing = true;

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

// 刷新成功后,把队列里的请求都重试了
requestsQueue.forEach(cb => cb(newToken));
requestsQueue = [];
} catch (err) {
// 刷新也失败了,清空队列并跳转登录
requestsQueue.forEach(cb => cb(null));
requestsQueue = [];
window.location.href = '/login';
return Promise.reject(err);
} finally {
isRefreshing = false;
}
}

// 如果正在刷新,把当前请求塞进队列,等刷新成功后重试
return new Promise((resolve, reject) => {
requestsQueue.push((token) => {
if (token) {
// 更新原请求的 Authorization 头
originalRequest.headers['Authorization'] = Bearer ${token};
resolve(api(originalRequest));
} else {
reject(error);
}
});
});
}

return Promise.reject(error);
}
);


几个关键点说明一下:

第一个请求401时,isRefreshing从false变成true,同时把请求塞进队列。然后axios重试这个请求时会走else分支,也进队列等待。等refreshToken完成,队列里的请求一个个用新token发出去。

这里用Promise把异步操作串起来了,避免重复刷新的问题。

如果刷新token本身也失败了(比如refresh token也过期了),那就直接跳登录页吧,别挣扎了。
点赞
2026-03-20 07:00