"用户反馈说用着用着就跳回登录页了,但明明没过期啊。"——这是某次需求评审会上,产品经理转述的客诉。排查下来原因很简单:Access Token有效期2小时,用户操作间隔超过2小时后再点按钮,Token已经过期,接口返回401,前端直接跳转登录页。
为什么需要无感刷新
让用户频繁重新登录是体验灾难,尤其是后台管理系统这种长时间使用的场景。无感刷新的目标是:Token过期时自动用Refresh Token换取新的Access Token,整个过程用户完全无感知。
方案A:双Token + 响应拦截器
这是最常见的方案。Access Token短期有效(如2小时),Refresh Token长期有效(如7天)。在axios响应拦截器中捕获401状态码,自动调用刷新接口,拿到新Token后重发失败的请求。
核心逻辑:拦截器检测到401 → 用Refresh Token请求新Access Token → 更新本地存储 → 用新Token重发原请求。代码层面大概30行就能搞定基础逻辑。
并发请求的问题
如果页面同时发了5个请求,Token过期后这5个请求都会收到401。如果不做控制,会同时触发5次刷新请求。解决办法是加一个isRefreshing标志位,第一个401触发刷新,后续的401请求先存入一个待重发队列,等刷新完成后统一重发。
方案B:定时器预刷新
思路不同——不等Token过期再刷新,而是在Token到期前主动刷新。比如Token有效期2小时,在1小时50分的时候就触发刷新。通常用setTimeout或者setInterval来实现。
优点是永远不会出现401的情况,请求不会失败。缺点也明显:如果用户关闭了页面或电脑休眠,定时器会暂停,恢复后可能已经过期了,还是得兜底处理401的情况。另外,频繁刷新会增加服务端压力。
方案C:请求前校验
在axios请求拦截器中,每次发请求前检查Token是否即将过期(比如剩余有效期不足5分钟)。如果快过期了,先刷新再发请求。
这个方案比定时器靠谱——只在有请求的时候才检查和刷新,不会浪费资源。但同样存在并发问题,需要用类似方案A的队列机制来控制。
三种方案对比
从实现复杂度看:方案A中等,方案B最简单,方案C中等偏上。从用户体验看:方案B最好(请求永远不失败),方案A和C偶尔会有一个请求的延迟(等刷新完成)。从可靠性看:方案A最稳,因为不依赖定时器,纯响应式处理。从服务端压力看:方案B最大,方案A最小。
实际项目怎么选
后台管理系统推荐方案A配合请求队列,稳定性优先。C端应用追求极致体验可以用方案B+方案A兜底的组合。如果团队对前端鉴权经验不多,方案A单独使用就够了,把并发队列逻辑写好基本不会出问题。
还有个容易忽略的安全细节:Refresh Token一定要存在httpOnly Cookie里,不要放localStorage。放localStorage的话一旦有XSS漏洞,攻击者能拿到Refresh Token,相当于拿到了长期登录权限。