无感知刷新 Token 的核心是拦截 401 响应后用 Refresh Token 获取新 Access Token 并重放请求:通过刷新锁(refreshingPromise)与 Promise 队列避免并发刷新,Refresh Token 存于 httpOnly Cookie,Access Token 存内存,刷新失败则登出,主动预刷新可减少 401。

无感知刷新 Token 的核心是:在请求因 Token 过期被拒绝(如返回 401)时,自动用 Refresh Token 换取新 Access Token,并重放原始请求,整个过程对用户和业务逻辑透明。
拦截 401 响应并触发刷新
所有 Ajax 请求统一走一个封装的请求函数(如基于 fetch 或 axios),在响应拦截器中判断状态码:
- 遇到
401且当前没有正在进行的刷新任务,就暂停后续请求、发起刷新 Token 请求 - 若已有刷新进行中,则让新请求等待刷新完成后的结果,避免并发多次刷新
- 刷新成功后,更新本地存储的 Access Token,并重试原请求;失败则跳转登录页
用 Promise 队列管理重试请求
刷新过程是异步的,多个同时失败的请求不能各自发刷新请求。推荐用一个“刷新锁” + 请求队列:
- 定义一个
let refreshingPromise = null标记是否正在刷新 - 当首次 401 到来,调用
refreshToken()并赋值给refreshingPromise - 后续 401 请求都
await refreshingPromise,复用同一结果 - 刷新完成后清空
refreshingPromise,供下次使用
安全存储与传递 Token
Access Token 通常存在内存或 sessionStorage 中(不存 localStorage 防 XSS);Refresh Token 建议存在 httpOnly Cookie 中(后端设置),前端无法直接读取,但会随请求自动携带:
立即学习“Java免费学习笔记(深入)”;
- 刷新接口(如
/auth/refresh)只需发送凭证(如 Cookie),无需前端暴露 Refresh Token - 新 Access Token 由后端通过响应头(如
X-Auth-Token)或响应体返回,前端提取后更新内存状态 - 所有后续请求自动带上新 Token(例如通过请求拦截器注入
Authorization头)
避免循环刷新与过期边界处理
Token 刷新不是万能的,需防止无限重试或已彻底失效的情况:
- 刷新接口本身也返回 401 或 403,说明 Refresh Token 已失效(过期/被撤销),应立即登出
- 为防止重放请求再次失败,可加简单计数或时间戳标记,单个请求最多重试一次
- 前端可缓存 Token 过期时间(
expires_in),在到期前 60 秒主动刷新,减少 401 触发频率
不复杂但容易忽略细节,关键是把错误处理、状态同步和请求调度串成闭环。


















