You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digital-rmb-backend/docs/superpowers/plans/2026-08-03-local-password-m...

7.5 KiB

PEVC 式本地密码镜像 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 让智云教师/学生在本系统完成 SSO 或 CAS 后,以其智云账号和密码直接登录本系统;本地只保存 BCrypt 密码哈希。

Architecture: 保留现有主平台 Token/CAS 身份校验。验证成功后,独立的只读凭据查询从 core_user.PASSWORD 获取当前密码(不进入 Token、日志或 API投影服务立即 BCrypt 编码并更新 sys_user.password_hash。本地 /api/v1/auth/login 仅校验本地哈希并签发本系统 JWT。

Tech Stack: Java 17, Spring Boot 4.1, Spring Security BCryptPasswordEncoder, MyBatis-Plus, Flyway, MySQL.

Global Constraints

  • 不修改 E:\javawork\tianze-pro;主平台数据源保持只读 SELECT。
  • 密码明文只在读取、BCrypt 编码和校验期间短暂存在禁止日志、响应、Token、快照表或迁移脚本保存。
  • 仅支持从智云识别出的 TEACHERSTUDENT;无用户/角色 CRUD API。
  • 参照 PEVC 的 UserDataMigrationAccountService:外部密码 BCrypt 后本地保存,本地登录只校验本地密码哈希。

Task 1: 主平台只读凭据查询与密码投影

Files:

  • Create: src/main/java/com/yau/digitalrmb/platformintegration/application/PlatformCredentialRepository.java
  • Create: src/main/java/com/yau/digitalrmb/platformintegration/domain/PlatformCredential.java
  • Modify: src/main/java/com/yau/digitalrmb/platformintegration/infrastructure/JdbcPlatformIdentityRepository.java
  • Modify: src/main/java/com/yau/digitalrmb/identity/application/PlatformIdentityProjectionService.java
  • Test: src/test/java/com/yau/digitalrmb/platformintegration/infrastructure/JdbcPlatformIdentityRepositoryTest.java
  • Test: src/test/java/com/yau/digitalrmb/identity/PlatformIdentityProjectionServiceTest.java

Interfaces:

  • Produces Optional<PlatformCredential> findCredential(long platformUserId).

  • PlatformCredential contains PlatformActor actor and String rawPassword; it has no toString logging implementation.

  • void project(PlatformCredential credential) stores passwordEncoder.encode(credential.rawPassword()) in sys_user.password_hash.

  • Write failing tests proving the credentials query only returns enabled teacher/student records and that projection stores a BCrypt value matching the source password.

  • Run the targeted tests; expect compilation failure for missing credential types.

  • Implement the secondary SELECT for core_user.PASSWORD, the BCrypt PasswordEncoder bean, and projection overload. The existing actor-only projection remains for metadata sync and must not change password hashes.

  • Run mvn '-Dtest=JdbcPlatformIdentityRepositoryTest,PlatformIdentityProjectionServiceTest' test -DforkCount=0 -B; expect success.

  • Commit with feat: mirror platform credentials securely.

Task 2: 生产本地用户名密码登录

Files:

  • Create: src/main/java/com/yau/digitalrmb/security/application/LocalAccountAuthenticationService.java
  • Modify: src/main/java/com/yau/digitalrmb/security/interfaces/AuthController.java
  • Delete: src/main/java/com/yau/digitalrmb/security/interfaces/BootstrapLoginController.java
  • Delete: src/main/java/com/yau/digitalrmb/security/application/BootstrapAdminAuthenticator.java
  • Modify: src/main/java/com/yau/digitalrmb/security/config/SecurityConfig.java
  • Test: src/test/java/com/yau/digitalrmb/security/AuthControllerTest.java

Interfaces:

  • JwtTokenService.Token login(String username, String rawPassword) resolves enabled sys_user, checks BCrypt, reads platform_user_snapshot, and issues a role-bearing JWT.

  • POST /api/v1/auth/login accepts existing LoginRequest and returns LoginResponse; it is anonymously reachable in every profile.

  • Replace the bootstrap-login regression test with a failing test that projects tzs001 using source password 123qwe, posts it to /api/v1/auth/login, and expects an access token.

  • Run mvn -Dtest=AuthControllerTest test -DforkCount=0 -B; expect failure because the production login service does not exist.

  • Implement local lookup, BCrypt verification, TEACHER/STUDENT role extraction, and the controller endpoint. Return UNAUTHORIZED for unknown, disabled, wrong-password, or non-projected users. Remove the bootstrap-only controller so no duplicate route exists.

  • Run mvn '-Dtest=AuthControllerTest,CurrentUserAndLogoutTest' test -DforkCount=0 -B; expect success.

  • Commit with feat: add local password login.

Task 3: SSO/CAS 密码初始化与 tzs001 启动初始化

Files:

  • Modify: src/main/java/com/yau/digitalrmb/platformintegration/interfaces/PlatformSsoController.java
  • Modify: src/main/java/com/yau/digitalrmb/platformintegration/interfaces/CasAuthenticationController.java
  • Create: src/main/java/com/yau/digitalrmb/identity/application/PlatformCredentialInitializer.java
  • Modify: src/main/java/com/yau/digitalrmb/platformintegration/config/PlatformIntegrationProperties.java
  • Modify: src/main/resources/application-local.yml
  • Test: src/test/java/com/yau/digitalrmb/platformintegration/interfaces/PlatformSsoControllerTest.java
  • Test: src/test/java/com/yau/digitalrmb/identity/PlatformCredentialInitializerTest.java

Interfaces:

  • Both successful SSO and CAS flows call credentialRepository.findCredential(actor.platformUserId()) followed by projectionService.project(credential) before issuing an exchange code.

  • platform-integration.local-login.initial-accounts defaults to an empty list; local profile contains tzs001 so startup initializes it from the platform read-only database.

  • Write failing tests proving SSO invokes credential projection and the initializer only initializes configured accounts.

  • Run target tests; expect missing initializer behavior.

  • Implement no-logging credential projection in both flows and the explicit configurable initializer. It must skip absent/disabled/non-teacher/non-student accounts without creating local users.

  • Run mvn '-Dtest=PlatformSsoControllerTest,PlatformCredentialInitializerTest,AuthControllerTest' test -DforkCount=0 -B; expect success.

  • Commit with feat: initialize local passwords from platform login.

Task 4: Verification and operations documentation

Files:

  • Modify: README.md

  • Modify: docs/superpowers/specs/2026-08-03-platform-sso-readonly-design.md

  • Test: src/test/java/com/yau/digitalrmb/security/EndToEndAuthenticationFlowTest.java

  • Write an end-to-end failing test: source credential → SSO projection → local /login/me; assert no raw password occurs in snapshots or JWT claims.

  • Implement only documentation and any wiring exposed by that test.

  • Document that local profile initializes tzs001 from the platform DB; never document or source-control a password.

  • Run mvn test -DforkCount=0 -B and mvn package -DskipTests -B; expect zero test failures and a generated executable JAR.

  • Commit with docs: document local password mirror login.

Plan Self-Review

  • PEVC alignment: Task 1 matches external credential migration with BCrypt; Task 2 matches PEVC local AccountService.login; Task 3 ensures SSO/CAS create or refresh local credentials.
  • Password handling: every task forbids raw password persistence outside the source read and BCrypt transformation.
  • Scope: no change is made to the upstream platform or to public user/role CRUD.