Summary: - Fix pg-boss queue conflict (duplicate key violation on queue_pkey) - Add global error listener to prevent process crash - Reduce connection pool from 10 to 4 - Add graceful shutdown handling (SIGTERM/SIGINT) - Fix researchWorker recursive call bug in catch block - Make screeningWorker idempotent using upsert Security Standards (v1.1): - Prohibit recursive retry in Worker catch blocks - Prohibit payload bloat (only store fileKey/ID in job.data) - Require Worker idempotency (upsert + unique constraint) - Recommend task-specific expireInSeconds settings - Document graceful shutdown pattern New Features: - PKB signed URL endpoint for document preview/download - pg_bigm installation guide for Docker - Dockerfile.postgres-with-extensions for pgvector + pg_bigm Documentation: - Update Postgres-Only async task processing guide (v1.1) - Add troubleshooting SQL queries - Update safety checklist Tested: Local verification passed
6.8 KiB
6.8 KiB
模块认证规范
本文档定义了业务模块如何正确使用平台认证能力,确保所有 API 都正确携带和验证用户身份。
1. 架构概览
┌─────────────────────────────────────────────────────────────┐
│ 前端 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ common/api/axios.ts ← 带认证的 axios 实例 │ │
│ │ framework/auth/api.ts ← Token 管理 (getAccessToken)│ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ Authorization: Bearer <token>
▼
┌─────────────────────────────────────────────────────────────┐
│ 后端 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ common/auth/auth.middleware.ts │ │
│ │ - authenticate: 验证 JWT Token │ │
│ │ - requirePermission: 权限检查 │ │
│ │ - requireRoles: 角色检查 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
2. 前端规范
2.1 使用带认证的 axios 实例(推荐)
// 导入带认证的 apiClient
import apiClient from '../../../common/api/axios';
// 使用方式与 axios 完全相同,自动携带 JWT Token
const response = await apiClient.get('/api/v2/xxx');
const response = await apiClient.post('/api/v2/xxx', data);
2.2 使用原生 fetch(需手动添加 Token)
import { getAccessToken } from '../../../framework/auth/api';
// 创建 getAuthHeaders 函数
function getAuthHeaders(): HeadersInit {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
const token = getAccessToken();
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return headers;
}
// 所有 fetch 请求使用 getAuthHeaders()
const response = await fetch(url, {
headers: getAuthHeaders(),
});
// 文件上传(不设置 Content-Type)
const token = getAccessToken();
const headers: HeadersInit = {};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(url, {
method: 'POST',
headers,
body: formData,
});
3. 后端规范
3.1 路由添加认证中间件
// 导入认证中间件
import { authenticate, requirePermission } from '../../../common/auth/auth.middleware.js';
// 添加到路由
fastify.get('/xxx', { preHandler: [authenticate] }, handler);
// 需要特定权限
fastify.post('/xxx', {
preHandler: [authenticate, requirePermission('module:action')]
}, handler);
3.2 控制器获取用户 ID
/**
* 获取用户ID(从JWT Token中获取)
*/
function getUserId(request: FastifyRequest): string {
const userId = (request as any).user?.userId;
if (!userId) {
throw new Error('User not authenticated');
}
return userId;
}
// 在控制器方法中使用
async function myHandler(request: FastifyRequest, reply: FastifyReply) {
const userId = getUserId(request);
// ... 使用 userId
}
3.3 JWT Token 结构
interface DecodedToken {
userId: string; // 用户ID
phone: string; // 手机号
role: string; // 角色
tenantId: string; // 租户ID
tenantCode?: string; // 租户Code
iat: number; // 签发时间
exp: number; // 过期时间
}
4. 检查清单
4.1 新模块开发检查清单
-
前端 API 文件
- 使用
apiClient或添加getAuthHeaders() - 文件上传单独处理(不设置 Content-Type)
- 导出函数不包含测试用 userId 参数
- 使用
-
后端路由文件
- 导入
authenticate中间件 - 所有需要认证的路由添加
preHandler: [authenticate] - 公开 API(如模板列表)可不添加认证
- 导入
-
后端控制器文件
- 添加
getUserId()辅助函数 - 移除所有
MOCK_USER_ID或硬编码默认值 - 使用
getUserId(request)获取用户 ID
- 添加
4.2 已完成模块状态
| 模块 | 前端 API | 后端路由 | 后端控制器 | 状态 |
|---|---|---|---|---|
| RVW | ✅ apiClient | ✅ authenticate | ✅ getUserId | ✅ |
| PKB | ✅ 拦截器 | ✅ authenticate | ✅ getUserId | ✅ |
| ASL | ✅ getAuthHeaders | ✅ authenticate | ✅ getUserId | ✅ |
| DC Tool B | ✅ getAuthHeaders | ✅ authenticate | ✅ getUserId | ✅ |
| DC Tool C | ✅ apiClient | ✅ authenticate | ✅ getUserId | ✅ |
| IIT | N/A (企业微信) | N/A | ✅ 企业微信userId | ✅ |
| Prompt管理 | ✅ getAuthHeaders | ✅ authenticate | ✅ getUserId | ✅ |
5. 常见错误和解决方案
5.1 401 Unauthorized
原因: 前端没有携带 JWT Token 或 Token 过期
解决:
- 检查前端 API 是否使用
apiClient或getAuthHeaders() - 检查 localStorage 中是否有
accessToken - 如果 Token 过期,尝试刷新或重新登录
5.2 User not authenticated
原因: 后端路由没有添加 authenticate 中间件
解决: 在路由定义中添加 preHandler: [authenticate]
5.3 TypeError: Cannot read property 'userId' of undefined
原因: 使用了错误的属性名(request.user.id 而非 request.user.userId)
解决: 使用 (request as any).user?.userId
6. 参考文件
- 前端 axios 实例:
frontend-v2/src/common/api/axios.ts - 前端 Token 管理:
frontend-v2/src/framework/auth/api.ts - 后端认证中间件:
backend/src/common/auth/auth.middleware.ts - 后端 JWT 服务:
backend/src/common/auth/jwt.service.ts