notice(后台登录): admin 登录态约2h过期需前端接入refresh静默续期(根因排查结论)
这个提交包含在:
父节点
27a6f1f8b8
当前提交
1b93c1b0ad
@ -0,0 +1,82 @@
|
|||||||
|
# notice(后台登录): admin 登录态约 2 小时就过期 → 需前端接入 refresh 静默续期(根因排查结论)
|
||||||
|
|
||||||
|
> **类型**: notice(后端无代码改动;双 token 机制后端早已就绪,缺前端接入)
|
||||||
|
> **关联 PR**: 无(后端不改);双 token 机制为工单 #2309 既有能力
|
||||||
|
> **日期**: 2026-05-26
|
||||||
|
> **影响接口**: `POST /admin/auth/login`(登录,已返回 refreshToken) + `POST /admin/auth/refresh`(刷新)
|
||||||
|
> **接收方**: mmg
|
||||||
|
> **前端**: **必须接入**(否则管理员每约 2 小时必被强制弹「登录已过期」一次)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 背景 / 现象
|
||||||
|
|
||||||
|
运营反馈正式后台管理端「登录状态老短时间就过期」,弹窗提示「登录已过期 / invalid token」,重新登录后用一会儿又掉。
|
||||||
|
|
||||||
|
经后端彻底排查(正式环境实测),结论如下:
|
||||||
|
|
||||||
|
- admin 的 **access token 固定 2 小时过期**(绝对时间,不随操作滑动续期)。正式 nacos 未单独配置过期时长,走后端默认 7200 秒。
|
||||||
|
- 后端**双 token 机制完整且正常**:登录已返回 `refreshToken`(有效期 30 天),`/admin/auth/refresh` 接口实测可正常用 refreshToken 换发新 access。
|
||||||
|
- JWT 签名/验签、网关校验**均正常**,排除密钥/配置问题。
|
||||||
|
- **唯一缺口在前端**:hl-ui 全量检索 `refreshToken` / `auth/refresh` **零命中** —— 前端登录后**没有保存 refreshToken,也没有任何静默续期逻辑**。access token 一过 2 小时,前端收到 401 就直接弹「登录已过期」让用户重新登录。
|
||||||
|
|
||||||
|
即:双 token 续期闭环的「后端发券」已就绪,「前端用券续期」这一段没做,导致 access 一过期只能重新登录。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 后端接口契约(已就绪,可直接接)
|
||||||
|
|
||||||
|
### 1. 登录 `POST /admin/auth/login`(已存在,无变化)
|
||||||
|
|
||||||
|
响应 `data` 字段(`AdminLoginResponse`):
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `token` | string | access token,有效期见 `accessTokenExpiresIn` |
|
||||||
|
| `refreshToken` | string | **刷新令牌,前端必须保存**(此前可能被前端忽略丢弃) |
|
||||||
|
| `accessTokenExpiresIn` | number | access 有效期**秒数**,当前 = `7200`(2 小时) |
|
||||||
|
| `refreshTokenExpiresIn` | number | refresh 有效期**秒数**,当前 = `2592000`(30 天) |
|
||||||
|
| `adminId` / `username` / `role` / `roleName` / `roles` / `avatar` / `mobile` 等 | - | 用户信息(不变) |
|
||||||
|
|
||||||
|
### 2. 刷新 `POST /admin/auth/refresh`(换发新 access)
|
||||||
|
|
||||||
|
- **无需 Authorization 头**(网关已放行该路径)
|
||||||
|
- 请求体:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "refreshToken": "登录时保存的 refreshToken" }
|
||||||
|
```
|
||||||
|
|
||||||
|
- 响应 `data` 结构**与登录响应完全一致**(同样是 `AdminLoginResponse`),其中:
|
||||||
|
- `token`:**新的 access token**(前端用它替换旧 access)
|
||||||
|
- `refreshToken`:**新的 refresh token —— 会轮换!与旧的不同,前端必须一并更新保存**(实测每次 refresh 都会返回新串,旧串随后失效)
|
||||||
|
- `accessTokenExpiresIn` / `refreshTokenExpiresIn`:同上
|
||||||
|
|
||||||
|
- 失败(refreshToken 过期 / 无效 / 已被新登录覆盖)时返回非 200 业务码,此时才真正需要跳登录页。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 前端需要做的事
|
||||||
|
|
||||||
|
1. **保存 refreshToken**:登录成功后,把 `data.refreshToken` 与 access token 一起存起来(同 access 的存储介质)。
|
||||||
|
2. **静默续期**(二选一或叠加):
|
||||||
|
- 方案 A(推荐,无感):根据 `accessTokenExpiresIn` 设定时器,在 access 临近过期(如剩余 5 分钟)时,后台静默调 `/admin/auth/refresh` 换新,**不打扰用户**。
|
||||||
|
- 方案 B(兜底):在 axios 响应拦截器中,捕获 access 过期的 401(`Invalid token`),**先尝试用 refreshToken 调 `/admin/auth/refresh`**,成功则用新 access **重放原失败请求**;只有 refresh 也失败时,才走现有的「登录已过期」弹窗 + 跳登录页逻辑(`src/utils/request.js` 现有 401 分支)。
|
||||||
|
3. **每次 refresh 后更新存储**:用返回的新 `token` 和新 `refreshToken` 覆盖旧值(refreshToken 会轮换,务必更新,否则下次续期会拿到失效的旧串)。
|
||||||
|
4. **并发请求处理**:多个请求同时 401 时,只发起一次 refresh,其余请求挂起等新 token 后重放(避免重复刷新)。现有代码已有 `isHandling401` 单飞标志,可复用同样思路做 refresh 单飞。
|
||||||
|
5. **登出 / refresh 失败**:清空 access + refreshToken,跳登录页(沿用现有逻辑)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 后端状态
|
||||||
|
|
||||||
|
- 后端**无需任何改动**,`/admin/auth/refresh` 与登录返回 refreshToken 均已在正式环境就绪并实测通过。
|
||||||
|
- 如前端短期内无法接入,后端可作为**临时缓解**调大 access 有效期(改正式 nacos `admin-access-ttl-seconds` 并重启),但这会拉长 token 泄露窗口,**非长久之计**,正解仍是前端接入 refresh 续期。是否启用此临时方案由后端按需评估。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 验证方式(前端接入后)
|
||||||
|
|
||||||
|
1. 登录后等 access 过期(可临时把判定阈值调短便于测试),期间持续操作,应**全程无「登录已过期」弹窗**,网络面板可见自动发起的 `/admin/auth/refresh`。
|
||||||
|
2. 手动篡改本地 access 为过期串发请求,应触发静默 refresh + 原请求重放成功。
|
||||||
|
3. 把本地 refreshToken 也置为无效,再触发,应正常落到「登录已过期」弹窗 + 跳登录页。
|
||||||
正在加载...
x
在新工单中引用
屏蔽一个用户