正常 JWT 请求流程 sequenceDiagram participant User as 用户 participant Frontend as 前端(浏览器/App) participant Auth as 认证服务器 participant Backend as 后端API服务器 Note over User,Backend: 1. 登录阶段 User->>Frontend: 输入用户名/密码 Frontend->>Auth: POST /login (凭证) Auth->>Auth: 验证凭证 Auth-->>Frontend: 返回 access_token + refresh_token Frontend->>Frontend: 存储token(内存/localStorage) Frontend-->>User: 登录成功 Note over User,Backend: 2. 正常请求阶段 User->>Frontend: 请求受保护资源 Frontend->>Backend: GET /api/resourceAuthorization: Bearer access_token Backend->>Backend: 验证access_token签名和过期时间 Backend-->>Frontend: 返回请求的资源 Frontend-->>User: 展示数据 Note over User,Backend: 3. Access Token过期 User->>Frontend: 继续请求 Frontend->>Backend: GET /api/resourceAuthorization: Bearer access_token(已过期) Backend-->>Frontend: 401 Unauthorized (token过期) Frontend->>Frontend: 检测到401,触发刷新逻辑 Note over Frontend,Auth: 4. 刷新Token阶段 Frontend->>Auth: POST /refreshrefresh_token Auth->>Auth: 验证refresh_token Auth-->>Frontend: 返回新的 access_token(可选的新refresh_token) Frontend->>Frontend: 更新存储的access_token Frontend->>Backend: 重试原请求(新access_token) Backend-->>Frontend: 返回请求的资源 Frontend-->>User: 展示数据 为什么不能只靠JWT refresh token 发出去后后端不保存,会变成 不可控的长期通行证 无法主动踢人下线 无法注销单点设备 无法判断 token 是否被盗用(攻击者拿到了 refresh token 可以一直刷新 access token) 无法实现会话管理 使用 refresh tokens 表后的流程图 sequenceDiagram participant User as 用户 participant Frontend as 前端 participant Auth as 认证服务器 participant DB as 数据库(refresh_tokens表) participant Backend as 后端API服务器 Note over User,DB: 1. 登录阶段(签发token + 入库) User->>Frontend: 输入用户名/密码 Frontend->>Auth: POST /login Auth->>Auth: 验证凭证 Auth->>DB: 生成唯一refresh_token_idINSERT INTO refresh_tokens(user_id, token_hash, expires_at, device_info, ip_address, revoked) DB-->>Auth: 插入成功 Auth->>Auth: 生成access_token + refresh_token(refresh_token包含id引用) Auth-->>Frontend: 返回access_token + refresh_token Frontend->>Frontend: 存储token Frontend-->>User: 登录成功 Note over User,Backend: 2. 正常请求(与之前相同) User->>Frontend: 请求资源 Frontend->>Backend: GET /api/resourceAuthorization: Bearer access_token Backend->>Backend: 验证access_token Backend-->>Frontend: 返回资源 Note over Frontend,DB: 3. Access Token过期 → 刷新 Frontend->>Backend: GET /api/resource (access_token过期) Backend-->>Frontend: 401 Unauthorized Frontend->>Auth: POST /refreshrefresh_token Note over Auth,DB: 4. 刷新验证(多步校验) Auth->>Auth: 解析refresh_token,提取token_id Auth->>DB: SELECT * FROM refresh_tokensWHERE id = token_id DB-->>Auth: 返回记录 Auth->>Auth: 校验清单: Note over Auth: ✅ token_hash是否匹配✅ 是否过期 (expires_at > now())✅ 是否被撤销 (revoked = false)✅ 用户是否仍有效✅ 设备信息是否一致(可选) alt 校验全部通过 Auth->>DB: UPDATE refresh_tokensSET last_used_at = now(), last_used_ip = current_ipWHERE id = token_id Auth->>Auth: 生成新access_token(可选:延长refresh_token有效期) Auth-->>Frontend: 返回新access_token Frontend->>Frontend: 更新access_token Frontend->>Backend: 重试原请求(新token) Backend-->>Frontend: 返回资源 else 校验失败 Auth-->>Frontend: 401/403 (刷新失败) Frontend->>Frontend: 清除所有token Frontend->>User: 跳转登录页 end Note over User,DB: 5. 主动登出 User->>Frontend: 点击登出 Frontend->>Auth: POST /logoutrefresh_token Auth->>DB: UPDATE refresh_tokensSET revoked = trueWHERE id = token_id Auth-->>Frontend: 登出成功 Frontend->>Frontend: 清除本地token Frontend-->>User: 已登出 Note over User,DB: 6. 安全场景:密码修改 User->>Frontend: 修改密码 Frontend->>Auth: POST /change-password Auth->>DB: UPDATE refresh_tokensSET revoked = trueWHERE user_id = current_user_id Note over DB: 撤销该用户所有refresh_token(强制所有设备重新登录) Auth-->>Frontend: 密码修改成功 Frontend->>Frontend: 清除本地token Frontend-->>User: 请重新登录