签入版本
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
# 5.1 用户认证(JWT 双令牌)
|
||||
|
||||
:::tip 概述
|
||||
JWT(JSON Web Token)是系统核心认证机制。采用双令牌设计:access_token 用于接口认证(2 小时),refresh_token 用于令牌刷新(7 天)。无状态、可水平扩展,配合 jti 黑名单实现可控的会话撤销。
|
||||
:::
|
||||
|
||||
## 双令牌机制
|
||||
|
||||
```text
|
||||
┌──────────┐ ┌──────────┐
|
||||
│ 用户登录 │──── username ────►│ │
|
||||
│ │──── password ────►│ auth │
|
||||
│ │──── code/key ────►│ .Login()│
|
||||
└──────────┘ └────┬─────┘
|
||||
│
|
||||
┌─────────────────┼─────────────────┐
|
||||
▼ ▼ ▼
|
||||
access_token refresh_token user info
|
||||
(2小时有效) (7天有效) (基础信息)
|
||||
```
|
||||
|
||||
### 两种令牌的区别
|
||||
|
||||
| 特性 | access_token | refresh_token |
|
||||
|------|-------------|---------------|
|
||||
| 用途 | 接口认证(每个 API 请求携带) | 刷新令牌(仅在 access_token 过期时使用) |
|
||||
| 有效期 | 2 小时(7200 秒,`jwt.accessTokenExpire`) | 7 天(604800 秒) |
|
||||
| claims.typ | `access` | `refresh` |
|
||||
| 传递方式 | `Authorization: Bearer <token>` | POST `/api/oauth2/token` 请求体 |
|
||||
| 校验方 | JWT 中间件 | logic/auth Refresh |
|
||||
|
||||
## Claims 结构(utility/jwt,真实实现)
|
||||
|
||||
```text
|
||||
算法 HS256;密钥 jwt.secret;签发者 jwt.issuer(默认 easygoadmin)
|
||||
|
||||
Claims(golang-jwt/v5 + RegisteredClaims):
|
||||
user_id / tenant_id / username / realname / typ(access|refresh)
|
||||
RegisteredClaims:jti(随机)、iss、iat、exp(exp 必填,Parse 强校验)
|
||||
```
|
||||
|
||||
关键 API:
|
||||
|
||||
```text
|
||||
Sign(ctx, tokenType, userID, tenantID, username, realname) → (token, claims, err)
|
||||
Parse(ctx, tokenStr) HS256 方法限定(防算法混淆)+ 签名/过期/签发者校验,
|
||||
失败统一 401 "Token无效或已失效"
|
||||
ExpireDuration(ctx, typ) 有效期(黑名单 TTL 计算用)
|
||||
```
|
||||
|
||||
## 令牌全生命周期
|
||||
|
||||
```text
|
||||
1. 登录
|
||||
POST /api/login {username, password, code, key}
|
||||
│ 签发 access_token + refresh_token
|
||||
▼
|
||||
前端存储(access 用于后续请求,refresh 用于续期)
|
||||
|
||||
2. 请求 API
|
||||
GET /api/user/page
|
||||
Authorization: Bearer <access_token>
|
||||
│ JWT 中间件:Parse → 黑名单 Exists → 注入身份
|
||||
▼
|
||||
controller → service → logic → 响应
|
||||
|
||||
3. access 到期(前端响应拦截器检测 401)
|
||||
POST /api/oauth2/token {grant_type: refresh_token, refresh_token: <refresh>}
|
||||
│ 校验 typ=refresh → 旧 refresh 已黑?→ 轮换(旧黑签新)
|
||||
▼
|
||||
返回新令牌对(旧 access 自然过期,旧 refresh 入黑名单防重放)
|
||||
|
||||
4. 登出 / 强退
|
||||
GET /api/logout 或 forceLogout
|
||||
│ 当前 access jti 入黑名单(TTL=剩余有效期)
|
||||
│ 强退额外拉黑 refresh jti(防被踢会话经 refresh 复活)
|
||||
▼
|
||||
MarkOffline:在线会话注销
|
||||
```
|
||||
|
||||
## 黑名单机制
|
||||
|
||||
```text
|
||||
键:easygoadmin:token:blacklist:{jti} = "1"
|
||||
TTL:令牌剩余有效期(过期即令牌本身已失效,无需主动清理)
|
||||
|
||||
写入时机:
|
||||
* 登出(Logout)
|
||||
* refresh 轮换(旧 refresh 拉黑)
|
||||
* RemoveToken 指定撤销(DELETE /api/oauth2/remove/:token)
|
||||
* 在线用户强退(access + refresh 一并拉黑)
|
||||
|
||||
读取方:JWT 中间件每请求 Exists 判定,命中即 401
|
||||
```
|
||||
|
||||
## 令牌刷新与撤销接口
|
||||
|
||||
```text
|
||||
POST /api/oauth2/token(公开)Refresh:
|
||||
* grant_type=password 或无 refresh_token → 兼容密码模式,复用 Login
|
||||
* refresh_token 模式:
|
||||
- Parse 校验 + typ 必须为 refresh(1008)
|
||||
- 旧 refresh 已在黑名单 → 1008(防重放)
|
||||
- 旧 refresh 入黑名单(轮换)
|
||||
- 签发新 access+refresh,MarkOnline 刷新在线登记
|
||||
- 返回同登录格式四字段
|
||||
|
||||
GET /api/oauth2/check/token 登录可用;JWT 中间件通过即 {valid:true, userId}
|
||||
DELETE /api/oauth2/remove/:token 登录可用;解析指定 token 的 jti 入黑名单
|
||||
GET /api/logout 当前 jti 入黑名单 + MarkOffline + 登录日志(log_type=2)
|
||||
```
|
||||
|
||||
## 登录响应格式(OAuth2 惯例,命名例外)
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIs…",
|
||||
"refresh_token": "eyJhbGciOiJIUzI1NiIs…",
|
||||
"expires_in": 7200,
|
||||
"token_type": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
::: warning 命名例外
|
||||
这是全系统唯一使用下划线命名的响应(其余入出参一律驼峰)。前端按 `data.access_token` 读取,**勿改驼峰**。
|
||||
:::
|
||||
|
||||
## 安全设计要点
|
||||
|
||||
+ HS256 + `WithValidMethods` 限定,防算法混淆攻击(none/RS256 降级)
|
||||
+ exp 必填强校验;iss 与配置比对
|
||||
+ 双令牌分工明确:access 不能当 refresh 用(typ 校验)
|
||||
+ 黑名单 TTL 精确等于剩余有效期,存储无冗余
|
||||
+ 无服务端 Cookie 会话——会话固定攻击面不存在
|
||||
+ 配置项:`jwt.secret`(生产必须改)、`jwt.issuer`、`accessTokenExpire`、`refreshTokenExpire`
|
||||
Reference in New Issue
Block a user