切换日光/暗黑模式
019. Token 刷新排队与真实后端验证
学习目标
这一节继续完善 token 服务,解决多个请求同时触发刷新 token 的问题,并切换到真实后端验证。
学完后,你应该能理解:
- 前端 mock 接口如何帮助验证登录流程;
- access token 过期后为什么会触发 refresh 接口;
- 多个请求同时刷新 token 会产生什么问题;
- 为什么刷新 token 需要排队;
Promise和延迟对象如何协调等待中的请求;- 关闭 mock 后如何用真实后端验证登录态。
路由可以做转发
登录页实际路径可以是带 layout 的路径,例如 /public/login。
如果觉得路径不够简洁,也可以做路由转发,让 /login 指向真实登录页。
这种思路和 Nginx 里的路径转发很像:
- 用户访问一个短路径;
- 前端路由规则匹配它;
- 实际渲染另一个页面。
这只是用户体验层面的调整,不影响 token 逻辑本身。
mock 接口的作用
前端先 mock 三类接口:
- 登录接口;
- 刷新 token 接口;
- 当前用户信息接口。
有了 mock 后,可以验证:
- 登录按钮是否调用接口;
- 登录成功后是否保存 token;
- 用户信息是否能读取;
- 登录失败时是否提示;
- token 过期后是否进入刷新流程。
在浏览器 Network 里未必能看到真实网络请求,因为 mock 会在前端拦截请求。可以通过日志确认 mock 函数是否被触发。
模拟 token 快速过期
为了测试刷新逻辑,可以把 access token 有效期改得很短。
例如把有效期设成 5 秒。
这样登录后稍等几秒,再触发多个接口请求,就能模拟:
- access token 已经过期;
- refresh token 还没过期;
- 前端需要调用 refresh 接口换新 token。
这种方式比等真实 30 分钟或 2 小时更适合开发调试。
多请求同时刷新 token 的问题
页面初始化时,可能会同时发出多个请求。
例如:
- 查询当前用户;
- 查询菜单;
- 查询列表;
- 查询字典。
这些请求都会先去拿 token。
如果 access token 刚好过期,所有请求都会发现“需要刷新 token”。如果不做控制,就可能同时发起多次 refresh 请求。
结果会变成:
- 一个页面触发三个业务请求;
- 三个请求都调用 refresh;
- 后端收到多次刷新请求;
- 每次返回的 token 可能还不一样;
- 后续请求使用不同 token,状态容易混乱。
正确目标是:多个请求同时到达时,只发起一次 refresh。
用刷新状态做互斥
可以用一个变量记录当前是否正在刷新 token。
例如:
ts
let isRefreshing = false;第一个请求发现需要刷新时:
- 把
isRefreshing设为true; - 发起 refresh 请求;
- 拿到新 token;
- 通知等待中的请求;
- 把
isRefreshing设回false。
后进来的请求发现 isRefreshing 已经是 true,就不要再发 refresh,而是进入等待队列。
等待队列
等待队列里保存的是还没拿到新 token 的请求。
这些请求需要等第一个 refresh 请求结束。
如果 refresh 成功:
- 队列里的请求都拿到同一个新 access token;
- 它们继续发送各自的业务请求。
如果 refresh 失败:
- 队列里的请求都失败;
- 前端清理 token;
- 用户重新登录。
这就是刷新 token 的排队机制。
延迟对象和 Promise
课程里用到类似 deferred 的写法,把 Promise 的 resolve 和 reject 暴露出来。
普通 Promise 创建后,通常只能在构造函数内部调用 resolve。
延迟对象会把它拆成三部分:
promise:给等待者 await;resolve:刷新成功时调用;reject:刷新失败时调用。
这样队列里的请求可以先拿到一个 Promise 等待,等刷新请求结束后,再由 token 服务统一决定它们成功还是失败。
刷新流程的完整判断
最终 token 服务大致要按这个顺序走:
- 如果当前已经在刷新,进入等待队列;
- 如果没有 access token,要求登录;
- 如果 access token 没过期,直接返回;
- 如果 refresh token 也过期,要求登录;
- 如果 refresh token 可用,开始刷新;
- 刷新成功后保存新 token;
- 通知等待队列;
- 清空队列并重置刷新状态。
这套逻辑的核心,是把“拿 token”变成一个可靠的异步服务。
业务请求只关心最终能不能拿到 token,不需要自己处理刷新细节。
用真实后端验证
mock 流程跑通后,可以关闭 mock,改为请求真实后端。
需要确认:
- 前端接口地址指向真实后端;
- 后端服务已启动;
- 登录接口返回字段格式正确;
- token 字段名和前端一致;
- refresh 接口参数格式正确;
- 当前用户接口能识别 Authorization 头。
如果真实后端返回 401、422 或字段格式不对,先看请求参数和响应结构,不要急着改 token 队列逻辑。
本地后端可能有冷启动
本地后端连接数据库时,有时第一次请求会慢一些。
可能表现为:
- 登录等待时间变长;
- 第一次用户信息请求较慢;
- 数据库连接初始化后才恢复正常。
这和前端 token 逻辑不是一类问题。排查时要分清:是前端没有带 token,还是后端启动、数据库连接、接口参数出了问题。
这一节的重点
这节的核心不是登录页面,而是认证请求的并发控制。
你需要理解:
- token 会过期;
- refresh 可以换新 token;
- 多个请求可能同时触发 refresh;
- refresh 应该只执行一次;
- 其他请求应该排队等结果;
- mock 和真实后端都要验证。
这套能力做好后,后面所有受保护接口都会更稳定。