2026-04-30-REQ-USR-004.md 4.79 KB

req_id: REQ-USR-004 date: 2026-04-30

spec_ref: docs/superpowers/specs/2026-04-30-REQ-USR-004.md

REQ-USR-004 用户登录 Implementation Plan

Execution: Parent skill feature-tdd executes this plan task-by-task.

Goal: Phase A 实现 POST /api/usr/auth/login + 失败计数锁定 + JWT 双 token;Phase B 闭环 stub permitAll → authenticated。


Schema 改动

无。

文件变更清单

新增

  • backend/src/main/java/com/xly/erp/module/usr/dto/LoginDTO.java
  • backend/src/main/java/com/xly/erp/module/usr/vo/LoginVO.java
  • backend/src/main/java/com/xly/erp/module/usr/vo/UserBriefVO.java
  • backend/src/main/java/com/xly/erp/module/usr/security/LoginAttemptStore.java
  • backend/src/main/java/com/xly/erp/module/usr/controller/AuthController.java
  • backend/src/main/java/com/xly/erp/common/security/JwtAuthenticationEntryPoint.java
  • backend/src/test/java/com/xly/erp/module/usr/security/LoginAttemptStoreTest.java
  • backend/src/test/java/com/xly/erp/module/usr/controller/AuthControllerIT.java

修改

  • common/security/JwtUtil.java — 加 signRefresh(userNo) + 区分 access/refresh TTL
  • common/security/SecurityConfig.java — 重写路径规则 + 注册 entryPoint
  • module/usr/mapper/UserMapper.java — 加 selectByUserName + updateLastLoginDate
  • module/usr/service/UserService.java + impl — 加 login(LoginDTO)
  • module/mod/service/impl/ModuleServiceImpl.java — 移除 stub fallback
  • module/usr/service/impl/UserServiceImpl.java — 移除 stub fallback(create + update)
  • ModuleControllerIT (4 处) + UserControllerIT (3 处) — 改 stub 期望

任务步骤

Phase A: 登录接口本体

Task 1: JwtUtil 加 signRefresh

  • Duration REFRESH_TTL = Duration.ofDays(30) + sign(userNo, ttl) 通用方法 + signRefresh(userNo) 包装;现有 sign(userNo)Duration.ofHours(8)
  • 单测:signRefresh_signsWithLongerTtl
  • Commit: feat(usr): jwtutil signRefresh REQ-USR-004

Task 2: LoginAttemptStore + 单测

  • 新建 @Component LoginAttemptStorerecordFailure(userNo) → newCount / isLocked(userNo) → Optional<Long secondsRemaining> / clearFailures(userNo);常量 MAX_ATTEMPTS=5 / LOCK_DURATION=Duration.ofMinutes(15)
  • 单测 5 用例覆盖隔离 / 累加 / 锁定 / 自动解锁 / 清空
  • Commit: feat(usr): login attempt store + lock logic REQ-USR-004

Task 3: UserMapper 加 selectByUserName + updateLastLoginDate

  • @Select("SELECT ... FROM tUser WHERE sUserName = #{name}") User selectByUserName(String name)
  • @Update("UPDATE tUser SET tLastLoginDate = #{ts} WHERE iIncrement = #{id}") int updateLastLoginDate(Integer id, LocalDateTime ts)
  • IT 2 用例
  • Commit: feat(usr): mapper selectByUserName + updateLastLoginDate REQ-USR-004

Task 4: LoginDTO + LoginVO + UserBriefVO + UserService.login + 单测

  • 6 个 service 单测(spec 列表)
  • Commit: feat(usr): login service + dto/vo REQ-USR-004

Task 5: AuthController + IT (5 用例)

  • 新建 AuthController @RequestMapping("/api/usr/auth")@PostMapping("/login")
  • IT 5 用例覆盖 spec
  • Commit: feat(usr): auth controller + login it REQ-USR-004

Phase B: Stub 闭环

Task 6: SecurityConfig 收紧 + AuthenticationEntryPoint

  • JwtAuthenticationEntryPoint:未认证写 JSON Result.fail(20001, "未认证") + status 200
  • SecurityConfig:
    • 移除 /api/mod/** + /api/usr/** permitAll
    • /api/usr/auth/login permitAll
    • anyRequest().authenticated()
    • exceptionHandling(eh -> eh.authenticationEntryPoint(authEntryPoint))
  • 移除 // REQ-MOD-001 stub: see USR-004 follow-up 注释
  • Commit: refactor(usr): tighten security to authenticated REQ-USR-004

Task 7: ModuleServiceImpl + UserServiceImpl 移除 stub fallback

  • ModuleServiceImpl#createsCreatedBy = SecurityContextHelper.currentUserNo(),去掉 ?: stub.getStubUserNo();同 UserServiceImpl#create + update
  • 不破坏 StubSecurityProperties bean 本身(仍可保留以防其他用途;本 REQ 仅停止 fallback 引用)
  • 单测调整:MOD-001 createWithValidDto_persistsWithStandardCols 期望 sCreatedBy 不再是 STUB_ADMIN(而要在测试里手动 SecurityContextHolder.set principal);其他类似
  • Commit: refactor(usr): remove stub fallback in services REQ-USR-004

Task 8: 修改现有 stub IT 期望

  • ModuleControllerIT 4 条:postWithoutJwt_permitAllStub_returns200_andCreatedBySTUBADMIN / putWithoutJwt_* / deleteWithoutJwt_* / getWithoutJwt_* → 改期望 code=20001,DB 无新增行
  • UserControllerIT 3 条:postWithoutJwt_* / putWithoutJwt_* / getWithoutJwt_* → 改期望 code=20001
  • 全量回归绿
  • Commit: test(usr): update stub regression to authenticated REQ-USR-004

提交计划

8 commits:6 feat + 2 refactor + 1 test(或合并为更少 commit 视进度)。