docs: 添加鉴权体系设计文档,更新认证相关文档
- 新增 12-鉴权体系设计.md,详细描述 JWT 双 token 轮转认证机制 - 更新架构设计文档,补充认证设计章节的安全机制和配置说明 - 更新接口文档,补充 Refresh Token Rotation 安全机制和前端集成示例 - 更新文档索引,添加新文档的推荐阅读顺序
This commit is contained in:
@@ -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
|
||||
#### 刷新 Token(Refresh 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);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
#### 登出
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user