token过期无感刷新的核心是拦截401、用refreshingpromise串行控制刷新、缓存待重试请求并统一重放;需统一捕获响应、同步更新token、动态注入请求头,并处理并发、失败、跨tab等边界情况。

Token 过期后实现“无感刷新”与请求重试,核心是拦截失败响应、自动刷新 Token、缓存待重试请求、再统一重放——关键不在“立刻重发”,而在“排队+串行+状态同步”。
监听 401 并触发刷新流程
所有请求(尤其 axios/fetch 封装层)需统一捕获 401 响应。检测到后不直接报错,而是判断是否已在刷新中:
- 用一个 Promise 变量(如 refreshingPromise)标记刷新状态;
- 若为空,调用刷新接口获取新 Token,并将返回的 Promise 赋值给它;
- 若非空,后续 401 请求直接 await 这个 Promise,避免并发刷新;
- 刷新成功后,更新本地存储(localStorage / httpOnly 配合内存缓存)和请求头默认配置。
暂存失败请求并批量重试
遇到 401 时,不能立即重发原请求,而应将其“挂起”:
- 维护一个请求队列(数组或 Set),存入原始 config、resolve/reject 回调、甚至拦截器上下文;
- 刷新成功后,遍历队列,用新 Token 重新发起每个请求(注意保留 method、data、headers 等);
- 重试时建议加简单防抖:比如同一 url + method 组合在 100ms 内只重试一次,避免重复提交;
- 对已取消的请求(如页面卸载、组件 unmount),需在重试前检查 abortSignal 或标记位,跳过执行。
确保 Token 同步与请求头自动注入
前端多个地方可能读取 Token(请求头、WebSocket 认证、图片上传 header),必须保证一致性:
- 封装一个 getToken() 方法,优先读内存缓存,再 fallback 到 localStorage;
- 刷新成功后,立即更新内存 token,并写入 localStorage(注意避免 race condition,例如用 async/await 顺序写入);
- 所有请求发送前,通过拦截器动态注入 Authorization 头,而不是初始化时静态设置;
- 若使用 httpOnly Cookie 存 token,前端无需管理 token 字符串,但仍需监听 401 并触发刷新逻辑(后端会自动 set-cookie)。
边界情况处理要到位
真实场景中容易忽略这些细节:
- 并发请求爆炸:5 个请求同时 401,只允许 1 次刷新,其余 await 同一个 Promise;
- 刷新本身失败:清除 refreshingPromise,清空队列,跳转登录页或弹出提示;
- 重试后仍失败:比如新 token 也过期(时间差/时钟不同步),则不再重试,直接登出;
- 跨 Tab 同步:监听 storage 事件,在其他 tab token 更新时主动 reload 或同步内存 token。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











