Skip to content

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;

第一个请求发现需要刷新时:

  1. isRefreshing 设为 true
  2. 发起 refresh 请求;
  3. 拿到新 token;
  4. 通知等待中的请求;
  5. isRefreshing 设回 false

后进来的请求发现 isRefreshing 已经是 true,就不要再发 refresh,而是进入等待队列。

等待队列

等待队列里保存的是还没拿到新 token 的请求。

这些请求需要等第一个 refresh 请求结束。

如果 refresh 成功:

  • 队列里的请求都拿到同一个新 access token;
  • 它们继续发送各自的业务请求。

如果 refresh 失败:

  • 队列里的请求都失败;
  • 前端清理 token;
  • 用户重新登录。

这就是刷新 token 的排队机制。

延迟对象和 Promise

课程里用到类似 deferred 的写法,把 Promise 的 resolvereject 暴露出来。

普通 Promise 创建后,通常只能在构造函数内部调用 resolve

延迟对象会把它拆成三部分:

  • promise:给等待者 await;
  • resolve:刷新成功时调用;
  • reject:刷新失败时调用。

这样队列里的请求可以先拿到一个 Promise 等待,等刷新请求结束后,再由 token 服务统一决定它们成功还是失败。

刷新流程的完整判断

最终 token 服务大致要按这个顺序走:

  1. 如果当前已经在刷新,进入等待队列;
  2. 如果没有 access token,要求登录;
  3. 如果 access token 没过期,直接返回;
  4. 如果 refresh token 也过期,要求登录;
  5. 如果 refresh token 可用,开始刷新;
  6. 刷新成功后保存新 token;
  7. 通知等待队列;
  8. 清空队列并重置刷新状态。

这套逻辑的核心,是把“拿 token”变成一个可靠的异步服务。

业务请求只关心最终能不能拿到 token,不需要自己处理刷新细节。

用真实后端验证

mock 流程跑通后,可以关闭 mock,改为请求真实后端。

需要确认:

  • 前端接口地址指向真实后端;
  • 后端服务已启动;
  • 登录接口返回字段格式正确;
  • token 字段名和前端一致;
  • refresh 接口参数格式正确;
  • 当前用户接口能识别 Authorization 头。

如果真实后端返回 401、422 或字段格式不对,先看请求参数和响应结构,不要急着改 token 队列逻辑。

本地后端可能有冷启动

本地后端连接数据库时,有时第一次请求会慢一些。

可能表现为:

  • 登录等待时间变长;
  • 第一次用户信息请求较慢;
  • 数据库连接初始化后才恢复正常。

这和前端 token 逻辑不是一类问题。排查时要分清:是前端没有带 token,还是后端启动、数据库连接、接口参数出了问题。

这一节的重点

这节的核心不是登录页面,而是认证请求的并发控制。

你需要理解:

  • token 会过期;
  • refresh 可以换新 token;
  • 多个请求可能同时触发 refresh;
  • refresh 应该只执行一次;
  • 其他请求应该排队等结果;
  • mock 和真实后端都要验证。

这套能力做好后,后面所有受保护接口都会更稳定。

AI Agent 课程学习文档。