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