后端中间件
后端使用 Nitro 的中间件系统在全球范围内执行安全、日志记录和运营逻辑。中间件文件位于 apps/backend-api/src/middleware/,并按数字/字母顺序执行。
中间件执行管线
文件名中的数字决定了执行顺序。这种层级结构对安全至关重要:
- 0.5.correlation.ts: 链路追踪设置。
- 0.maintenance.ts: 全局锁定开关。
- 1.logger.ts: 请求日志记录。
- 2.auth.ts: 身份认证与 RBAC。
- 2.5.csrf.ts: CSRF 保护。
- 3.ratelimit.ts: 速率限制。
1. 链路追踪 (0.5.correlation.ts)
目的: 为每个请求分配一个唯一的 ID,以便在日志和服务之间进行追踪。
- 逻辑:
- 检查请求头
X-Correlation-ID或X-Request-ID中是否存在 ID(对微服务很有用)。 - 如果缺失,则生成一个新的
UUID。 - 将该 ID 附加到
event.context。 - 在响应头中设置
X-Correlation-ID。
- 检查请求头
2. 维护模式 (0.maintenance.ts)
目的: 允许管理员锁定系统进行升级,同时保持自己可以访问。
- 配置键:
maintenance_mode(在SystemConfig表中)。 - 逻辑:
- 排除: 跳过
/api/health和/api/status,以便监控工具不会发出虚假报警。 - 检查全局
maintenance_mode设置。 - 如果已启用:
- 验证用户认证。
- 绕过: 如果用户拥有
super角色,则允许请求。 - 拦截: 对所有其他用户返回
503 Service Unavailable。
- 排除: 跳过
3. 操作日志 (1.logger.ts)
目的: 记录所有 API 交互的详细审计轨迹。
- 数据源: 从由其他中间件填充的
event.context中读取(用户、关联 ID、旧值/新值)。 - 存储: 写入
OperationLog数据库表。 - 逻辑:
- 排除: 跳过
/health,/test和/uploads/以减少噪音。 - 挂载到响应的
finish事件,以捕获最终的 HTTP 状态码和执行时长。 - 记录:用户、IP、路径、方法、参数和时长。
- 上下文: 如果由路由处理器设置,可以捕获
operationDescription(操作描述)、oldValue(旧值) 和newValue(新值)。
- 排除: 跳过
4. 身份认证与 RBAC (2.auth.ts)
目的: 核心安全守门人。处理身份识别(你是谁?)和权限控制(你能做这个吗?)。
- 逻辑:
- 公共路径: 跳过对登录、刷新、MFA 和健康检查接口的验证。
- 身份认证 (AuthN):
- 验证 JWT 访问令牌。
- 会话检查: 根据
UserSession表进行验证,确保会话未被撤销或过期。 - 更新会话的
lastActiveAt时间戳。
- 权限授权 (AuthZ):
- 超级管理员: 绕过所有权限检查。
- 路由匹配: 将请求路径(如
/api/users)与数据库中的ApiPermission记录匹配。 - 权限校验: 验证用户的角色是否链接到该
ApiPermission。 - 缓存: 使用 Valkey (Redis) 缓存角色权限查询结果以提高性能 (
perms:role:...)。
- 结果:
- 如果是无效令牌/会话,返回
401 Unauthorized。 - 如果角色没有权限,返回
403 Forbidden。
- 如果是无效令牌/会话,返回
5. CSRF 保护 (2.5.csrf.ts)
目的: 使用双重提交 Cookie 模式防止跨站请求伪造 (CSRF) 攻击。
- 模式: 同时要求一个 Cookie (
XSRF-TOKEN) 和一个匹配的请求头 (X-XSRF-TOKEN)。 - 逻辑:
- 排除:
- 安全方法:
GET,HEAD,OPTIONS。 - 公共路径: 登录、刷新、MFA。
- 上传:
/uploads/(通过其他方式处理)。
- 安全方法:
- 从 Cookie 和请求头中读取令牌。
- 验证:
- 两者必须同时存在。
- 两者必须完全匹配。
- 结果: 如果不匹配,返回
403 Forbidden。
- 排除:
6. 速率限制 (3.ratelimit.ts)
目的: 保护 API 免受滥用、暴力破解和 DoS 攻击。
- 存储: 使用
RateLimit表 (PostgreSQL)。 - 规则:
- 认证接口: 15 分钟内 100 次请求(严格)。
- 用户创建: 1 小时内 50 次请求(防垃圾)。
- 密码重置: 1 小时内 5 次请求(高安全性)。
- 文件上传: 1 小时内 100 次请求。
- 通用 API: 1 分钟内 60 次请求。
- 逻辑:
- 根据请求路径识别匹配的规则。
- 原子计数: 执行数据库 Upsert(插入或更新)以增加该 IP 地址的计数器。
- 执行:
- 如果计数 > 限制,返回
429 Too Many Requests。 - 返回
Retry-After(稍后重试) 消息。
- 如果计数 > 限制,返回