docs: plan local password mirror login

master
chenyuan 4 weeks ago
parent 4d49eaff20
commit 5de5b5d5c6

@ -0,0 +1,99 @@
# 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<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.
Loading…
Cancel
Save