服务端一直签 access(30 分钟)和 refresh(14 天)两个令牌,但两个客户端拿到 401 都是直接清 令牌跳登录页,refresh 从来没被用过——"14 天有效"只是纸面参数,真实寿命就是 30 分钟。 - web/client.ts:请求前剩不到 90 秒先换;撞 401 先换再重放一次原请求;并发共用一次刷新; 上传附件同一条路 - android/Api.kt:call 拆 call/callOnce,2002 时用 Mutex 串行刷新后重放 - 后端 /auth/refresh 看一眼账号状态:停用的账号不能靠 refresh 一直续(2012) - 新增 session-refresh.mjs:假 access + 真 refresh 要能留住人,两个都假才回登录页
This commit is contained in:
+8
-1
@@ -56,6 +56,10 @@
|
||||
`Authorization: Bearer <access_token>`。`access` 30 分钟,`refresh` 14 天。
|
||||
`refresh_token` 只能用于 `/auth/refresh`(服务端校验 `typ`)。
|
||||
|
||||
**客户端要自己续期,别等用户被踢出去**:access 剩不到 90 秒就先换一对,
|
||||
撞上 `401/2002` 也先用 refresh 换一对、把原请求重放一次,换不动才清令牌回登录页。
|
||||
每次 `/auth/refresh` 都会返回新的 refresh,所以只要 14 天内用过一次,登录态就一直往后顺延。
|
||||
|
||||
### 0.4 幂等
|
||||
|
||||
所有 `POST/PATCH/DELETE` 都可带 `Idempotency-Key: <uuid>`。
|
||||
@@ -146,7 +150,10 @@
|
||||
{ "refresh_token": "…" }
|
||||
```
|
||||
|
||||
返回同形状的 `tokens`。无状态 JWT,**服务端不维护黑名单**——token 泄露只能等过期,这是 M0 已知取舍。
|
||||
返回同形状的 `tokens`(**扁平**,没有 `tokens` 信封)。无状态 JWT,**服务端不维护黑名单**——
|
||||
token 泄露只能等过期,这是 M0 已知取舍;换来的是刷新不需要查库。
|
||||
|
||||
换的时候会看一眼账号状态:账号被停用 → `2012`(403),停用的人不能靠 refresh 一直续下去。
|
||||
|
||||
### `POST /auth/login/password`
|
||||
|
||||
|
||||
@@ -712,3 +712,30 @@
|
||||
- `web/e2e/knowledge-read.mjs` 直接打生产(106 的手机号 + 密码登录)8 项全绿,
|
||||
截图 `2.Design/screens/prod-knowledge-read-page.png`。
|
||||
- 脚本都换成了密码登录(`PASSWORD` env 优先),生产短信登录关着,别再指望发验证码。
|
||||
|
||||
## 12. 登录态续期(2026-11-03)
|
||||
|
||||
用户反馈:"web端登陆后好像没多久就退出登陆了,现在系统对登陆时限是怎么设计的?"
|
||||
|
||||
### 12.1 原来的设计(也就是问题所在)
|
||||
|
||||
- access 30 分钟、refresh 14 天,服务端两个都签好、都发给了客户端。
|
||||
- 但**没有任何客户端用过 refresh**:web 与安卓拿到 `401/2002` 都是直接清令牌、回登录页。
|
||||
- 结果"14 天有效"只是纸面参数:真实登录寿命就是 30 分钟,到点下一次请求就被扔出去,
|
||||
正在写的工单、正在编辑的知识都不会留下。
|
||||
|
||||
### 12.2 改法
|
||||
|
||||
- `web/src/lib/client.ts`:新增 `refreshSession()`(并发只发一次),请求前若 access 剩不到 90 秒先换;
|
||||
收到 401 先换再重放一次原请求,换不动才清令牌发 `authExpired`。`uploadFile` 走同一条路。
|
||||
- `android/app/src/main/java/com/dadao/task/data/Api.kt`:`call()` 拆成 `call` + `callOnce`,
|
||||
2002 时 `refreshTokens(stale)` 用 `Mutex` 串行刷新(等锁期间别人换好了就直接用新的),重放一次。
|
||||
- 后端 `/auth/refresh` 顺手补一条:账号被停用 → `2012`,别让停用账号靠 refresh 一直续。
|
||||
|
||||
### 12.3 验证
|
||||
|
||||
- `backend/tests/test_auth.py::test_disabled_account_cannot_refresh`(停用账号换令牌 → 403/2012),
|
||||
全量 **140 passed**、`ruff check` / `format --check` 全绿。
|
||||
- 新增 `web/e2e/session-refresh.mjs`(6 项):access 换成假串但 refresh 是真的 → 不被踢、首页正常、
|
||||
拿到新令牌、新令牌能调接口;access 与 refresh 都假 → 才回登录页;控制台零异常。
|
||||
- 安卓 `assembleDebug` 通过(JDK 17 + Gradle 8.9)。
|
||||
|
||||
Reference in New Issue
Block a user