← 返回内容列表
工程实践

Token 刷新与请求队列怎么处理

Token 刷新真正困难的地方不是发送刷新请求,而是多个请求同时失效时如何保持状态一致。

Token 刷新Axios 401请求队列JWT 过期刷新Axios 拦截器前端登录鉴权

先用白话理解

这篇内容在讲什么?

Token 像登录后的临时通行证。过期时要换新,同时让正在发送的请求排队等待。

问题

多个接口同时 401,重复刷新 Token 甚至死循环,如何处理?

每个失败请求独立刷新,没有共享刷新状态和重试标记。

解决方案
  1. 刷新请求保持单例
  2. 其他请求等待同一 Promise
  3. 成功后重放原请求
  4. 失败后清空身份
Axios 刷新与重试示例代码
type RetryConfig = AxiosRequestConfig & { _retried?: boolean }
let refreshPromise: Promise<string> | null = null

api.interceptors.response.use(undefined, async (error) => {
  const config = error.config as RetryConfig
  if (error.response?.status !== 401 || config._retried) {
    return Promise.reject(error)
  }

  config._retried = true
  try {
    refreshPromise ??= refreshToken()
    const token = await refreshPromise
    config.headers = { ...config.headers, Authorization: `Bearer ${token}` }
    return api.request(config)
  } catch (refreshError) {
    clearSession()
    return Promise.reject(refreshError)
  } finally {
    refreshPromise = null
  }
})
01

刷新动作必须单例化

第一个 401 请求启动刷新流程,后续失败请求进入等待队列,避免并发发送多个刷新请求导致凭据相互覆盖。

02

队列需要明确成功与失败出口

刷新成功后使用新 Token 重放请求;刷新失败则统一清理身份状态、拒绝队列并引导重新登录,不能让 Promise 永久悬挂。

03

重试必须避免循环

刷新接口自身、已经重试过的请求和明确无权限的响应都不应再次进入刷新流程。

  • 给重试请求增加内部标记
  • 刷新接口使用独立请求实例
  • 退出登录时主动取消等待队列

结语

判断最终要回到真实体验

技术方案没有脱离场景的标准答案。把用户任务、业务边界、开发成本和长期维护放在一起评估,才有可能做出稳定、清晰且经得起变化的页面。

照着做的步骤

  1. 第一个 401 启动刷新
  2. 其他请求进入等待队列
  3. 成功后使用新 Token 重试
  4. 失败后统一退出登录

容易踩的坑

  • 并发刷新多次
  • 失败后队列永久等待
  • 请求陷入无限重试

术语不用怕

  • Token:识别登录用户的凭据
  • 401:身份无效或过期的响应
  • 请求队列:暂时等待处理的请求集合