docs: 添加鉴权体系设计文档,更新认证相关文档

- 新增 12-鉴权体系设计.md,详细描述 JWT 双 token 轮转认证机制
- 更新架构设计文档,补充认证设计章节的安全机制和配置说明
- 更新接口文档,补充 Refresh Token Rotation 安全机制和前端集成示例
- 更新文档索引,添加新文档的推荐阅读顺序
This commit is contained in:
hhs
2026-06-20 16:51:03 +08:00
parent 3572b867c0
commit 4ff8cec312
4 changed files with 658 additions and 5 deletions

View File

@@ -279,13 +279,27 @@ Client Server
#### 认证方式
需要认证的接口在请求头携带 JWT access token
采用 **JWT 双 token 轮转认证机制**。详细设计见 [鉴权体系设计](./12-鉴权体系设计.md)。
**Token 类型**
- **access_token**短期令牌15 分钟),用于 API 认证和 WebSocket 连接
- **refresh_token**长期令牌7 天),用于刷新 access_token
**请求头格式**
```
Authorization: Bearer <access_token>
```
未认证或 token 过期时返回 `401 Unauthorized`
**认证流程**
1. 用户登录后获取 access_token + refresh_token
2. 请求受保护接口时携带 access_token
3. access_token 过期时,使用 refresh_token 刷新获取新的 token pair
4. refresh_token 采用轮转机制,每次刷新后旧 token 失效
**WebSocket 认证**
- 连接地址:`ws://host/ws?token=<access_token>&conversation_id=<uuid>`
- HTTP Upgrade 前校验 token
- 校验失败返回 401 Unauthorized
#### 错误响应格式
@@ -380,7 +394,7 @@ interface LoginRequest {
| 400 | `INVALID_INPUT` | 请求参数缺失或格式错误 |
| 401 | `INVALID_CREDENTIALS` | 用户名或密码错误 |
#### 刷新 Token
#### 刷新 TokenRefresh Token Rotation
```
POST /api/auth/refresh
@@ -397,12 +411,56 @@ interface RefreshRequest {
**成功响应** `200 OK`:同 `AuthResponse` 结构(返回新的 access_token + refresh_token旧 refresh_token 失效——Token 轮转)。
**安全机制**
- **Token 轮转**:每次 refresh 都会生成新的 token pair旧 refresh_token 立即失效
- **复用检测**:如果检测到已删除的 refresh_token 被复用,立即吊销该用户的所有 refresh_token
- **强制重新登录**:吊销后,该用户所有设备都需要重新登录
**错误响应**
| 状态码 | code | 场景 |
|--------|------|------|
| 401 | `INVALID_TOKEN` | refresh_token 无效或已过期 |
**前端集成示例**
```typescript
// axios 响应拦截器
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
// 如果是 401 且不是 refresh 请求,尝试刷新 token
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const refreshToken = getRefreshToken();
const response = await api.post('/api/auth/refresh', {
refresh_token: refreshToken,
});
const { access_token, refresh_token } = response.data;
setAccessToken(access_token);
setRefreshToken(refresh_token);
// 重试原始请求
originalRequest.headers.Authorization = `Bearer ${access_token}`;
return api(originalRequest);
} catch (refreshError) {
// 刷新失败,跳转登录页
clearTokens();
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
```
#### 登出
```