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/specs/2026-08-03-platform-sso-rea...

138 lines
8.7 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 数字人民币系统:单后端 SSO 与只读用户设计
## 1. 目标与边界
数字人民币系统在现有 `digital-rmb-backend` 中同时实现三个登录入口:
- 主平台跳转后的单点登录;
- 用户直接访问系统后的学校 CAS 统一认证登录。
- 使用主平台账号密码的本地直接登录。
主平台不做改造。用户、教师/学生身份及角色均以主平台为唯一来源;数字人民币系统仅维护受控的只读快照,不提供用户、角色或用户角色的人工增删改接口。业务角色只有 `TEACHER``STUDENT`
“只读”指面向用户、管理端和公开 API 只读。内部同步任务可写入本地镜像表,否则无法完成主平台数据复制。
## 2. 已验证的现状
主平台使用学校 CAS 完成统一认证,在登录后签发自定义 Token它不是 OAuth2/OIDC 身份提供方。
- CAS 配置位于主平台 `web/src/main/resources/application.properties`
- CAS Filter 配置位于 `web/src/main/java/cn/jlw/Interceptor/CasConfig.java`
- 自定义 Token 的生成和 HMAC 验证位于 `web/src/main/java/cn/jlw/token/TokenService.java`
- CAS 用户到教师/学生的映射位于 `web/src/main/java/com/ibeetl/jlw/service/CasUserLoginService.java`
- 主平台用户、学生、教师表分别为 `core_user`、`student`、`teacher`。
主平台 Token 的 HMAC 密钥由教师或学生记录的 `addTime` 派生,不能使用 PEVC 示例中的固定共享密钥方式验签。PEVC 示例采用的也是“父系统 JWT + 自定义共享密钥”协议,并非 OAuth2。
## 3. 总体架构
不新增独立部署的认证服务。认证、防腐适配和本系统 JWT 都在现有 Spring Boot 后端内实现。
```
主平台 ──携带自定义 Token 跳转──> digital-rmb-backend ──签发本系统 JWT──> 前端
学校 CAS ──CAS Ticket 认证────────> digital-rmb-backend ──签发本系统 JWT──> 前端
主平台只读库 ──密码明文(瞬时)─────> BCrypt ──> sys_user.password_hash
digital-rmb-backend ──SELECT only──────────────────────────> 主平台数据库
```
主平台 Token 适配与 CAS 协议放入 `platform-integration` 防腐层。`identity` 限定上下文维护只读快照与角色投影;`security` 限定上下文签发、校验和撤销数字人民币系统自己的 JWT。业务域只依赖 `CurrentActor`(主平台用户 ID、教师/学生角色、状态),不依赖 CAS 或主平台 Token 细节。
## 4. 登录流程
### 4.1 主平台单点登录
1. 主平台既有跳转链接访问 `GET /api/v1/auth/sso?token=...`
2. 后端按 Token 中未验签的用户 ID 和身份类型定位主平台候选记录;
3. 后端读取候选教师或学生记录的 `addTime`,按主平台算法完成 HMAC 验签;
4. 后端读取主平台用户状态、教师/学生资料、角色及密码,刷新本地只读快照,并将密码立即 BCrypt 编码为本地哈希;
5. 后端生成仅一次、短时有效的登录兑换码,重定向到配置的前端回调地址;
6. 前端调用 `POST /api/v1/auth/session/exchange` 兑换本系统 JWT 与刷新令牌。
### 4.2 直接访问的独立登录
1. 用户访问 `GET /api/v1/auth/cas/login`
2. 后端将浏览器重定向至学校 CAS 登录页,并使用固定的回调地址作为 CAS `service`
3. CAS 将 `ticket` 回调至 `GET /api/v1/auth/cas/callback`
4. 后端调用学校 CAS 的 `serviceValidate` 接口验证 Ticket取得学校账号
5. 后端以学校账号查询主平台 `core_user`,仅接受教师或学生,再刷新快照及本地密码哈希;
6. 后端创建登录兑换码,前端兑换本系统 JWT 与刷新令牌。
用户已在学校 CAS 或主平台登录时,第 2 步无感完成;没有 CAS 会话时,用户在学校认证页输入统一账号密码。
### 4.3 本地账号密码登录
1. 用户调用 `POST /api/v1/auth/login`,提交主平台账号与密码;
2. 本系统仅以 `sys_user.password_hash` 的 BCrypt 哈希校验密码,成功后签发本系统 JWT
3. 初次使用前,用户通过主平台 SSO/CAS 成功登录时会初始化本地哈希;`local` 环境还会在启动时为配置账号 `tzs001` 尝试初始化;
4. 密码变更后,下一次成功 SSO/CAS 登录会刷新本地哈希。密码明文绝不进入快照表、JWT、响应、日志或迁移脚本。
### 4.4 退出
`POST /api/v1/auth/logout` 只撤销本系统刷新令牌与本地会话;默认不退出学校 CAS 或主平台,避免跨系统连带登出。
## 5. 本地只读投影
| 表 | 用途 | 写入者 |
| --- | --- | --- |
| `platform_user_snapshot` | 主平台用户 ID、账号、姓名、状态、同步时间 | 内部同步任务 |
| `role` | 固定角色 `TEACHER`、`STUDENT` | 数据库迁移 |
| `user_role` | 主平台用户到教师/学生角色的镜像 | 内部同步任务 |
| `platform_identity_link` | 主平台用户 ID 与本系统主体的稳定映射 | 内部同步任务 |
| `auth_refresh_token` | 本系统可撤销刷新令牌摘要 | 认证模块 |
| `login_exchange_code` | 一次性前端兑换码摘要及过期时间 | 认证模块 |
角色映射以主平台 `core_user.job_type1` 为准:`JT_S_02` 映射为 `TEACHER``JT_S_03` 映射为 `STUDENT`。其他身份、已删除用户、禁用教师或禁用学生均拒绝登录。
主平台用户 ID 是稳定关联键,不按姓名、手机号或可变用户名合并用户。同步采用首次全量、定时增量和每次成功登录即时刷新;本系统的用户与角色接口均为只读。
## 6. 安全规则
### 6.1 主平台 Token
1. 限制 Token 长度、字符集和主平台约定的 JWT 加登录时间戳结构;
2. 未验签声明只用于查询候选记录;
3. 严格使用候选学生或教师记录的 `addTime` 重建 HMAC 密钥并验证签名;
4. 比较 Token 身份类型、主平台 `job_type1` 和目标资料表的一致性;
5. 检查状态,并对 Token 的 SHA-256 指纹作一次性消费;跳转 Token 最长有效窗口为 120 秒;
6. 验签后绝不透传主平台 Token业务接口只接受本系统 JWT。
### 6.2 URL 与前端回调
跳转 Token 仅由 SSO 入口接收,之后立即重定向到不含 Token 的 URL。相关响应设置 `Cache-Control: no-store``Referrer-Policy: no-referrer`;反向代理、访问日志和异常日志必须脱敏 `token` 参数。
前端回调地址只能来自白名单配置,禁止请求参数指定任意重定向地址。回调中只携带一次性兑换码,不能携带主平台 Token 或本系统 JWT。
### 6.3 CAS
CAS `service` 地址必须固定并使用 HTTPS。Ticket 仅能向配置的 CAS `serviceValidate` 地址验证XML 解析必须关闭外部实体和 DTD防止 XXE。CAS 返回的账号只是身份索引,仍必须通过主平台只读数据确认教师/学生角色和状态。
### 6.4 数据库权限
后端使用独立的主平台只读账号,仅对 `core_user`、`student`、`teacher` 及必要关联表授予 `SELECT`。禁止授予 `INSERT`、`UPDATE`、`DELETE`、`CREATE`、`ALTER` 或 DDL 权限。
本系统只允许主平台教师和学生使用本地账号密码登录。密码哈希仅由内部主平台凭据投影写入;不提供用户、角色、用户角色或密码的管理接口。主平台数据库账号除身份表外还需对 `core_user.PASSWORD` 保有只读 `SELECT` 权限。
## 7. 对外接口
- `GET /api/v1/auth/sso?token=...`:主平台单点登录入口;
- `GET /api/v1/auth/cas/login`:发起学校 CAS 认证;
- `GET /api/v1/auth/cas/callback?ticket=...`:固定 CAS 回调;
- `POST /api/v1/auth/login`:以已初始化的主平台账号密码直接登录;
- `POST /api/v1/auth/session/exchange`:以一次性兑换码换取本系统 JWT
- `GET /api/v1/auth/me`:读取当前用户与教师/学生角色;
- `POST /api/v1/auth/logout`:撤销本系统会话。
Swagger 只公开上述认证和当前用户读取接口;不公开用户、角色、用户角色 CRUD。
## 8. 验收与测试
- 有效主平台教师 Token 返回 `TEACHER`,学生 Token 返回 `STUDENT`
- 伪造、篡改、过期、重放、身份不匹配或状态异常的 Token 均被拒绝;
- CAS 有会话和无会话两种直接登录路径均可完成;
- CAS Ticket 验证失败、CAS 返回非教师/学生、主平台查询失败均返回受控错误;
- 本地账号密码仅校验 BCrypt 哈希密码明文不出现在本系统存储、JWT、响应或日志
- 登录兑换码一次性、短时有效,不能被重放;
- 主平台数据库账号仅能 SELECT任何写操作都失败
- 用户、角色和用户角色的写接口不存在或被拒绝;
- Swagger 不展示用户/角色 CRUD且不会记录 Token。