Files
AIclinicalresearch/docs/04-开发规范/10-模块认证规范.md
HaHafeng d595037316 feat(admin): Complete tenant management and module access control system
Major Features:
- Tenant management CRUD (list, create, edit, delete, module configuration)
- Dynamic module management system (modules table with 8 modules)
- Multi-tenant module permission merging (ModuleService)
- Module access control middleware (requireModule)
- User module permission API (GET /api/v1/auth/me/modules)
- Frontend module permission filtering (HomePage + TopNavigation)

Module Integration:
- RVW module integrated with PromptService (editorial + methodology)
- All modules (RVW/PKB/ASL/DC) added authenticate + requireModule middleware
- Fixed ReviewTask foreign key constraint (cross-schema issue)
- Removed all MOCK_USER_ID, unified to request.user?.userId

Prompt Management Enhancements:
- Module names displayed in Chinese (RVW -> 智能审稿)
- Enhanced version history with view content and rollback features
- List page shows both activeVersion and draftVersion columns

Database Changes:
- Added platform_schema.modules table
- Modified tenant_modules table (added index and UUID)
- Removed ReviewTask foreign key to public.users (cross-schema fix)
- Seeded 8 modules: RVW, PKB, ASL, DC, IIT, AIA, SSA, ST

Documentation Updates:
- Updated ADMIN module development status
- Updated TODO checklist (89% progress)
- Updated Prompt management plan (Phase 3.5.5 completed)
- Added module authentication specification

Files Changed: 80+
Status: All features tested and verified locally
Next: User management module development
2026-01-13 07:34:30 +08:00

6.8 KiB
Raw Blame History

模块认证规范

本文档定义了业务模块如何正确使用平台认证能力,确保所有 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 过期

解决:

  1. 检查前端 API 是否使用 apiClientgetAuthHeaders()
  2. 检查 localStorage 中是否有 accessToken
  3. 如果 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