# 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、快照表或迁移脚本保存。 - 仅支持从智云识别出的 `TEACHER`、`STUDENT`;无用户/角色 CRUD API。 - 参照 PEVC 的 `UserDataMigration` 与 `AccountService`:外部密码 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 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.