H0-03 JwtAuthGuard 增加用户状态检查 #48

Closed
opened 2026-05-24 21:49:32 +08:00 by wangdl · 2 comments
Owner

目标

修复当前 JwtAuthGuard 只验证 JWT 签名、不检查用户状态的漏洞。

背景说明

当前 JwtAuthGuard 验证 JWT 后不查询用户表,导致已禁用/已删除的用户仍可正常访问 /api/* 接口。这是 C 端安全基线问题。

模块职责

请设计和实现以下检查逻辑:

  1. 验证 JWT 后必须查询用户
  2. 用户不存在时拒绝访问
  3. deletedAt 不为空时拒绝访问
  4. 用户 status 非 ACTIVE 时拒绝访问
  5. 禁用用户不能继续访问任何 /api/* 接口
  6. 返回合适的错误码和提示信息

禁止事项

  • 禁止仅在登录时检查用户状态(每次请求都要检查)
  • 禁止把用户状态检查放在业务层而非 Guard 层
## 目标 修复当前 JwtAuthGuard 只验证 JWT 签名、不检查用户状态的漏洞。 ## 背景说明 当前 JwtAuthGuard 验证 JWT 后不查询用户表,导致已禁用/已删除的用户仍可正常访问 /api/* 接口。这是 C 端安全基线问题。 ## 模块职责 请设计和实现以下检查逻辑: 1. 验证 JWT 后必须查询用户 2. 用户不存在时拒绝访问 3. deletedAt 不为空时拒绝访问 4. 用户 status 非 ACTIVE 时拒绝访问 5. 禁用用户不能继续访问任何 /api/* 接口 6. 返回合适的错误码和提示信息 ## 禁止事项 - 禁止仅在登录时检查用户状态(每次请求都要检查) - 禁止把用户状态检查放在业务层而非 Guard 层
wangdl added this to the H0:iOS 对接阻断修复(P0) milestone 2026-05-24 21:49:32 +08:00
Author
Owner

H0-03 修复完成

问题

JwtAuthGuard 仅验证 JWT 签名,不检查用户是否被禁用或已删除。禁用/删除用户的 Token 仍可访问所有 CAPI 端点。

修改

文件 变更
src/common/guards/jwt-auth.guard.ts 注入 PrismaService,JWT 验证后查询用户,检查 deletedAtstatus
test/mocks/prisma.mock.ts 添加 TEST_USER / disabled-user / deleted-user 数据桩
test/h0.e2e-spec.ts 新增 H0-03 相关 4 个测试用例

行为变化

用户状态 修改前 修改后
active 200 200(不变)
disabled(status ≠ active) 200(漏洞) 401 "账号已被禁用"
deleted(deletedAt 不为空) 200(漏洞) 401 "账号不存在或已注销"
不存在 200(旧逻辑不解码查库) 401 "账号不存在或已注销"

与 AdminAuthGuard 对比

JwtAuthGuard 现在与 AdminAuthGuard(admin-auth.guard.ts:45-59)对齐,后者早已实现相同的用户状态检查。

测试

npx jest --config ./test/jest-e2e.json --testPathPatterns="h0" --forceExit  # 14/14
npx jest --config ./test/jest-e2e.json --testPathPatterns="m0" --forceExit  # 25/25, no regression
## ✅ H0-03 修复完成 ### 问题 JwtAuthGuard 仅验证 JWT 签名,不检查用户是否被禁用或已删除。禁用/删除用户的 Token 仍可访问所有 CAPI 端点。 ### 修改 | 文件 | 变更 | |------|------| | `src/common/guards/jwt-auth.guard.ts` | 注入 PrismaService,JWT 验证后查询用户,检查 `deletedAt` 和 `status` | | `test/mocks/prisma.mock.ts` | 添加 TEST_USER / disabled-user / deleted-user 数据桩 | | `test/h0.e2e-spec.ts` | 新增 H0-03 相关 4 个测试用例 | ### 行为变化 | 用户状态 | 修改前 | 修改后 | |----------|--------|--------| | active | 200 | 200(不变) | | **disabled(status ≠ active)** | **200(漏洞)** | **401 "账号已被禁用"** | | **deleted(deletedAt 不为空)** | **200(漏洞)** | **401 "账号不存在或已注销"** | | 不存在 | 200(旧逻辑不解码查库) | 401 "账号不存在或已注销" | ### 与 AdminAuthGuard 对比 JwtAuthGuard 现在与 AdminAuthGuard(`admin-auth.guard.ts:45-59`)对齐,后者早已实现相同的用户状态检查。 ### 测试 ```bash npx jest --config ./test/jest-e2e.json --testPathPatterns="h0" --forceExit # 14/14 npx jest --config ./test/jest-e2e.json --testPathPatterns="m0" --forceExit # 25/25, no regression ```
Author
Owner

已实现 — 无需修改

该需求在 JwtAuthGuard 中已经完整实现:

文件: src/common/guards/jwt-auth.guard.ts:47-60

const user = await this.prisma.user.findUnique({
    where: { id: payload.sub },
    select: { id: true, status: true, deletedAt: true, role: true, email: true },
});

if (!user || user.deletedAt) {
    throw new UnauthorizedException("账号不存在或已注销");
}

if (user.status !== "active") {
    throw new UnauthorizedException("账号已被禁用");
}

覆盖的检查项

  • JWT 验证后查询用户
  • 用户不存在 → 401
  • deletedAt 不为空 → 401
  • status 非 active → 401
  • 每次请求都检查(Guard 层,非业务层)
    admin token 被拒绝(payload.type === "admin" → 401)

E2E 测试: test/h0.e2e-spec.ts H0-03 章节覆盖了 active/disabled/deleted/non-existent/admin 五种场景。

建议关闭此 issue。

## 已实现 ✅ — 无需修改 该需求在 `JwtAuthGuard` 中已经完整实现: **文件**: `src/common/guards/jwt-auth.guard.ts:47-60` ```typescript const user = await this.prisma.user.findUnique({ where: { id: payload.sub }, select: { id: true, status: true, deletedAt: true, role: true, email: true }, }); if (!user || user.deletedAt) { throw new UnauthorizedException("账号不存在或已注销"); } if (user.status !== "active") { throw new UnauthorizedException("账号已被禁用"); } ``` **覆盖的检查项**: - ✅ JWT 验证后查询用户 - ✅ 用户不存在 → 401 - ✅ deletedAt 不为空 → 401 - ✅ status 非 active → 401 - ✅ 每次请求都检查(Guard 层,非业务层) ✅ admin token 被拒绝(`payload.type === "admin"` → 401) **E2E 测试**: `test/h0.e2e-spec.ts` H0-03 章节覆盖了 active/disabled/deleted/non-existent/admin 五种场景。 建议关闭此 issue。
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wangdl/api-server#48
No description provided.