# R01-A2 独立代码复验报告(第二批:8 个 Controller 请求身份与 requestId 收束) 日期:2026-09-17 分支:`refactor/r01-structure-performance` HEAD:`52c8026b2e56dae7dbfae6a828a9efe23e180445`(复验全程未变) 复验对象:`docs/r01-a2-request-context-migration-report.md` 性质:**仅复验** —— 未修改生产代码、未暂存、未提交、未推送 --- ## 0. 结论 > **R01-A2独立代码复验通过,可以进入精确暂存、本地提交与推送检查点。** **FAIL = 0,SKIP = 0。** 所有 FAIL 判据逐条核对后均未触发。 --- ## 1. 基线 | 检查项 | 结果 | |---|---| | 分支 | `refactor/r01-structure-performance` ✔ | | HEAD | `52c8026b2e56dae7dbfae6a828a9efe23e180445` ✔ | | 暂存区 | 空 ✔ | | 实际差异文件 | **8 个 Controller + `docs/backend-development-roadmap.md`**,加上 `GlobalExceptionHandler.java`(stat 缓存标记)✔ | | `GlobalExceptionHandler.java` 内容 | 工作区 blob `1fdc341e…` == HEAD blob `1fdc341e…` —— **内容零差异** ✔ | | `git diff --check` | 无空白错误 ✔ | | 历史未跟踪文件 | 未操作,240 个完整保留 ✔ | --- ## 2. 静态审查 ### 2.1 禁用模式命中 | Controller | 命中 | |---|---| | ApplicationController | **0** | | CompanyController | **0** | | MessagingController | **0** | | OfferController | **1** —— `:210 Long.valueOf(part)`,**业务参数解析**(路径/查询参数),非身份解析 | | InterviewController | **0** | | CandidateScoringController | **0** | | CandidateScoreCardController | **0** | | CompanyScoringDashboardController | **0** | 模式:`SecurityContextHolder|Authentication|anonymousUser|Long\.valueOf|Long\.parseLong|getRequestId|resolveRequestId|X-Request-Id|new CurrentUserAccessor|new RequestIdAccessor` **私有身份/requestId 方法残留:0** ✔ ### 2.2 注入方式 8 个 Controller 均为: ```java private final com.mydreamjob.security.CurrentUserAccessor currentUserAccessor; private final com.mydreamjob.common.logging.RequestIdAccessor requestIdAccessor; ``` - **同一个** `CurrentUserAccessor` / `RequestIdAccessor`(全限定名一致)✔ - **无** `@Autowired(required=false)`、`@Nullable`、`ObjectProvider`、`Optional<…>`、`new CurrentUserAccessor()`、`new RequestIdAccessor()` 回退 ✔ (grep 命中的 `required = false` 全部是 `@RequestParam`,与依赖注入无关) ### 2.3 调用顺序与契约 | 核查项 | 结论 | |---|---| | 删除行中是否含角色/归属检查 | **否**(`assertCompany*`/`assertStudent*`/`FORBIDDEN`/`findCompanyByOwnerUserId`/`getRoles` 均未被删除)✔ | | 新增行中是否引入角色/归属判断 | **否** ✔ | | Service 调用参数 | 未变(仍为 `userId`、`req`、`AuditContext.from(request)` 等)✔ | | 响应结构 | 未变(`ApiResponse.ok(...)`)✔ | | DTO | 未变 ✔ | | 改动性质 | 仅:import 增删、字段新增、`resolveUserId(…)` → `currentUserAccessor.requireUserId()`、`getRequestId(…)` → `requestIdAccessor.get(request)`、私有方法删除 ✔ | --- ## 3. 独立边界测试 新增**两个独立测试类**(不复用实施者的测试与断言):`A2IndependentVerificationTest`(11 例)、`A2AccessorWiringProbeTest`(3 例)。 ### 3.1 身份 | 覆盖 | 结果 | |---|---| | 8 端点 × 8 非法主体(`anonymousUser`、空串、空白、`not-a-number`、`abc123`、**20 位溢出 `12345678901234567890`**、`0`、`-1`) | **全部 401**,**无 500** ✔ | | 匿名 `AnonymousAuthenticationToken`、null 认证 | 401 ✔ | | 合法正数主体 `"7"` | 不被身份层判为未登录(非 401)✔ | | 响应体检查方式 | **解析 JSON 后只检查 `code`/`message`**,未对整响应做数字子串搜索 ✔ | | 不回显异常类/堆栈/principal | `message` 精确属于 `{authentication required, not authenticated}`;无 `Exception`/`java.`/`principal`;无 `stackTrace`/`trace`/`principal` 字段 ✔ | ### 3.2 角色与归属 | 覆盖 | 结果 | |---|---| | STUDENT 访问企业操作(`GET /company/candidates`) | **403** ✔(既有 `ApplicationControllerTest.list_student_shouldReturn403` 亦通过) | | COMPANY 访问学生操作 | 企业账号在测试夹具下具备复合角色,端到端 403 由 Service 层产生;本次以**既有测试套件**(`*Application*Test` 等 630/630 全绿)作为该路径的回归证据 ✔ | | 无 token | 8 端点全部 **401**(filter chain 层)✔ | | 身份失败不被角色层覆盖 | 非法主体访问学生/企业端点均保持 **401**(非 403)✔ | | 资源不存在 | 既有 404 映射未改动(`resolveHttpStatus` 与 `NOT_FOUND` 注册表未变)✔ | | 合法用户可达 Service | `A2AccessorWiringProbeTest#legalPrincipalReachesService` 断言 `messagingService.listConversations(42L)` 被调用 ✔ | ### 3.3 requestId(Filter / Accessor 直接测试) | 覆盖 | 结果 | |---|---| | 合法值原样保留 | ✔ | | 空白 / 空串 / null 重新生成 | ✔ | | 超长(100 字符)规范化为 32 | ✔ | | CR / LF / CRLF 不进入结果 | ✔(`doesNotContain("\r")`、`doesNotContain("\n")`) | | 非法字符(`