2026年了,后台首屏几个接口一起报 401,连刷新 token 的请求也 401,是拦截器把自己也拦了吗?
结论先说:2026 年 Angular 项目里 token 一过期就疯狂重试,通常不是刷新接口本身坏了,而是两件事叠在一起:刷新 token 的请求也被同一个拦截器拦下,形成 401→刷新→又 401 的递归;首屏并发请求各自收到 401 后又没有排队,同时触发多次刷新。更稳的做法是让刷新请求走独立 HttpClient 或白名单跳过,并发 401 只允许一个刷新在飞,其余请求挂起,拿到新 token 后再重放。
先看三个触发点,别只盯着刷新接口
很多团队第一反应是查刷新接口返回值,但交付现场更常见的是拦截器没区分「业务请求」和「刷新请求」。先把触发点定位清楚,再谈排队,能少走一半弯路。
- 刷新请求被自己拦截:刷新 token 用的是同一个 HttpClient,拦截器把它也当成需要鉴权的请求,401 处理逻辑自己调用自己。
- 并发 401 各自刷新:后台首屏常见同时发 6~8 个接口,旧 token 过期后一起返回 401,每个都去调刷新接口,结果互相覆盖。
- 刷新失败后没停:刷新接口本身失败时,兜底逻辑又触发重试或路由跳转,路由守卫再发请求,形成另一条循环。
判断是否命中这三类问题,可以看浏览器网络面板:刷新接口短时间被调用多次,或刷新接口自身也返回 401,基本可以确认拦截器递归与并发未排队同时存在。
交付现场:首屏并发 401,用户被连续踢出
在一个后台管理系统项目里,约束很典型:仪表盘首屏并发 6~8 个接口,token 有效期偏短,联调环境里旧 token 过期后集中返回 401。原来的拦截器对每个 401 都直接调刷新接口,刷新请求又走同一个 HttpClient,日志里很快出现成串 401,用户被连续跳了三次登录页。
后来的做法是给刷新请求单独建一个不带该拦截器的 HttpClient 实例,同时在拦截器里按 URL 白名单跳过刷新与登录接口;并发 401 用单飞 Observable 加等待队列,队列超时放在 8~15 秒的经验区间。结果是并发 401 只触发一次刷新,原请求重放成功,用户不再被连续踢出。代价是拦截器多了一层状态管理,后续同事接手要先看注释,否则容易把白名单删掉,问题会重新出现。
三种做法怎么取舍:别为两个接口上队列
方案没有绝对好坏,关键看并发量和 token 生命周期。2026 年常见的三种做法对比如下,工作量为经验区间,视拦截器数量和并发场景而定。
- 方案 A 拦截器内直接刷新:常见 0.5~1 人天;适合接口少、并发低、token 有效期覆盖单次会话的小项目。不适合首屏多接口并发或 token 很短的后台系统。
- 方案 B 独立认证服务加单飞队列:常见 1~3 人天;适合多接口并发、需要无感续期的后台系统。要维护队列、超时和失败兜底,复杂度高一些。
- 方案 C 路由守卫预刷新:常见 0.5~1 人天;适合页面级跳转前续期。不适合页面内长时间停留、定时轮询或弹窗内连续请求的场景。
如果项目里已有统一认证服务,优先把单飞逻辑放进服务,而不是散在拦截器里;如果只是几个公开页面加一个登录态,方案 A 加白名单跳过通常够用,不必为排队引入额外状态。
上线前按这几条验收
验收 401 处理不能只看「能刷新」,要看并发和失败路径。下面几条可以作为可测口径。
- 并发 5 个请求同时 401 时,刷新接口只被调用 1 次,其余请求在队列里等待。
- 刷新请求不出现在拦截器的 401 递归日志里,刷新接口自身返回 401 时不再触发刷新。
- 刷新失败后只跳转 1 次登录页,等待队列里的请求直接失败返回,不重复跳转。
- 重放请求保留原 method、body 和业务头,只替换 Authorization;POST 不出现空 body。
- 等待队列有超时,常见 8~15 秒,避免刷新接口挂起时页面一直转圈。
常见坑集中在白名单和重放两处:白名单按 URL 包含匹配太宽,会把业务接口也跳过;重放时新建请求只带 URL 和 header,漏掉 body,POST 就变成空提交。另一个高频问题是刷新成功后没更新本地存储,用户手动刷新页面又拿到旧 token。
适用与不适用边界
适合上单飞刷新与等待队列的情况:后台系统首屏并发接口较多、token 有效期较短、需要用户无感续期、刷新接口本身也需要鉴权。公开内容页、以 Cookie 会话为主的站点,通常不必照搬。
不适合的情况也要写清楚:token 有效期覆盖单次使用且接口很少的小项目,上队列反而增加维护成本;页面内只有一两个请求、很少遇到并发 401 的场景,先做白名单跳过更划算。
边界句:如果项目只有 1~2 个接口且 token 有效期覆盖单次会话,先用白名单跳过刷新请求即可,单飞队列不是必选项。是否升级到队列,看并发 401 是否真实出现、是否造成重复刷新或连续踢登录,而不是看框架版本。
常见问题
Angular 拦截器里怎么让刷新 token 的请求不再次被拦截?
常见做法是给刷新请求单独建一个不带该拦截器的 HttpClient 实例,或在拦截器里按 URL 白名单和自定义头跳过;两者选其一,别只靠 URL 包含判断。
多个接口同时 401,怎么保证只刷新一次 token?
用一个共享的 refreshInFlight Observable 做单飞,第一个 401 发起刷新,其余请求订阅同一个流,等新 token 到了再重放,避免各自调用刷新接口。
刷新 token 失败后,为什么页面会连续跳好几次登录页?
并发 401 各自走了失败分支,每个都执行跳转。兜底逻辑要加已跳转标记,或只让刷新发起方处理跳转,其余等待请求直接失败返回。
拦截器里重放原请求,为什么 POST 的 body 丢了?
常见是重放时新建了请求只带 URL 和 header,没把原 request.body 和 method 带过去;用 clone 并显式设置 body 和 setHeaders 更稳妥。
用 HttpInterceptorFn 和类拦截器,处理 401 有区别吗?
函数式拦截器靠 inject 拿依赖,单飞状态要放在可注入服务里而不是模块级变量;类拦截器同理,差别主要在写法,并发排队逻辑一致。
如果接下来要改现有拦截器,建议先按「识别、排队、重放、兜底」四步做一次代码走查,再用并发 5 个 401 的联调场景验收。适用边界也要提前说清:公开页面和 Cookie 会话站点不必照搬单飞队列,按实际并发与 token 生命周期取舍即可。
-
2026年了,弹窗里点“获取验证码”,整张表单也跟着提交了,是按钮默认 type 在作怪吗?
日期:2026年10月30日 阅读:16
-
2026年了,AI 把官网首页出得挺快,目标机型上一开就图片错位、按钮点不到,兼容和性能这摊还该前端收吗?
日期:2026年10月29日 阅读:56
-
2026年了,述职被问前端体验架构师和普通前端差在哪,我拿什么证明才算数?
日期:2026年10月28日 阅读:34
-
2026年了,活动页倒计时锁屏几分钟再打开就慢了一大截,用户截图问是不是我们少给了时间,问题出在哪?
日期:2026年10月27日 阅读:92
-
2026年了,Uni-app 打的 App 在安卓上杀掉进程再打开就要重新登录,微信小程序里却还认得我,这两边的缓存不是同一个地方吗?
日期:2026年10月26日 阅读:125




