1. 新增 iwork 同步用户组织信息接口

2. 修复错误设置版本信息在 zt-dependencies 的 bug
This commit is contained in:
chenbowen
2025-11-20 18:27:01 +08:00
parent 52a0b561f9
commit 0b646295da
27 changed files with 2040 additions and 151 deletions

194
docs/外部单点登录.md Normal file
View File

@@ -0,0 +1,194 @@
# 外部单点登录External SSO接入说明
## 功能概述
- 支持外部系统携带一次性 token 跳转本系统,实现免密单点登录。
- 去除了历史实现中的 payload 解密、nonce 强校验、自动建号与邮箱匹配等逻辑,所有账号均需在本地预先存在并保持映射关系。
- 前后端新增 `/system/sso/verify` 校验能力,返回标准的 `AuthLoginRespVO` 令牌信息,并记录审计与登录日志。
- 通过 `ExternalSsoClient` 抽象外部接口调用,可按需自定义实现或复用默认 HTTP 客户端封装。
## 关键组件
### 后端
- `ExternalSsoServiceImpl`:单点登录主流程,实现参数校验、外部用户查询、本地账号匹配、令牌签发与日志记录。
- `ExternalSsoStrategy`:定义按来源系统拆分的策略接口,不同系统可实现自定义的拉取与匹配逻辑。
- `DefaultExternalSsoStrategy`:默认策略实现,复用配置化的 HTTP 客户端与匹配顺序,可按优先级被自定义策略覆盖。
- `ExternalSsoClient`:获取外部用户信息的接口抽象。
- `DefaultExternalSsoClient`:基于 `RestTemplate` 的默认实现,支持 Header/Query/Body 占位符渲染、重试、响应字段映射、代理配置。
- `ExternalSsoClientConfiguration`:通过 `@Configuration` 在缺省情况下注册 `DefaultExternalSsoClient` Bean允许业务自定义覆盖。
- `ExternalSsoProperties``external-sso.*` 配置项,包含开关、外部接口、账号映射、跨域等子配置;示例配置已同步到 `zt-module-system/zt-module-system-server/src/main/resources/application.yaml``zt-server/src/main/resources/application.yaml`
- `ExternalSsoVerifyReqVO``POST /system/sso/verify` 请求载荷。
- `ExternalSsoUserInfo`:外部用户标准化模型,包含基础字段与自定义属性集合。
### 前端
- `src/router/modules/remaining.ts`:新增隐藏路由 `/externalsso`,用于回调页面。
- `src/views/Login/ExternalSsoCallback.vue`:处理 URL 参数、调用校验接口、落地令牌、跳转目标页面,异常时提示并引导返回登录页。
- `src/api/login/index.ts`:新增 `externalSsoVerify` 方法,请求 `/system/sso/verify` 并返回 `TokenType`
## 调用流程
1. 外部系统完成本地认证后,构造 URL`{本系统域名}/#/externalsso?x-token={外部token}&target={回跳地址}` 并跳转,可选附带 `sourceSystem` 指定来源系统。
2. Vue 页面 `ExternalSsoCallback` 解析查询参数,优先读取 `x-token`(兼容历史的 `token` 参数)并校验是否存在;缺失时终止流程并提示错误。
3. 前端调用 `POST /system/sso/verify`,请求体:
```json
{
"token": "外部系统颁发的 token",
"targetUri": "/#/dashboard", // 可选
"sourceSystem": "partner-a" // 可选
}
```
4. `ExternalSsoServiceImpl#verifyToken` 执行以下步骤:
- 校验功能开关与 token 有效性。
- 基于 `sourceSystem` 选择匹配的 `ExternalSsoStrategy`,若无匹配直接返回来源系统不支持。
- 通过策略触发 `ExternalSsoClient` 拉取外部用户信息(默认调用配置的 HTTP 接口)。
- 按策略内定义的匹配顺序(默认外部 ID → 用户名 → 手机号)查找本地账号,未命中直接返回 "未找到匹配的本地用户"。
- 校验账号状态是否启用,签发 OAuth2 访问令牌并记录登录日志(类型 `LOGIN_EXTERNAL_SSO`)。
- 生成操作审计日志,记录外部响应摘要、映射账号、目标地址、来源系统等信息。
5. 前端拿到 `AuthLoginRespVO`,通过 `authUtil.setToken`/`setTenantId` 持久化,随后跳转到整理后的 `targetUri`(默认 `/`)。
6. 当流程出现异常(例如 token 缺失、外部接口失败、账号不存在)时,后端返回对应错误码,前端弹出提示并清除本地缓存。
下图为时序示意:
```text
外部系统 -> 浏览器 -> Vue /externalsso -> POST /system/sso/verify -> ExternalSsoService -> ExternalSsoClient -> 外部用户接口
\-> OAuth2TokenService -> 登录日志/审计日志
```
## 前端细节
- `ExternalSsoCallback.vue` 对 `target`/`targetUri`/`redirect` 参数做统一解码与归一化,支持携带绝对地址或 hash 路由,防止开放跳转漏洞。
- 解析 URL 中的 `sourceSystem`(兼容 `source`、`systemCode`)并透传给后端,便于在多来源系统场景下选择策略。
- 在成功回调后调用 `router.replace`,保证不会产生历史记录;失败时引导用户回到 `/login` 并附带原目标地址。
- 通过 `buildErrorMessage` 兼容后端返回的 `msg`、`Error` 对象或字符串,统一展示错误提示。
## 后端流程拆解
- **开关与参数校验**
- 关闭开关或缺少 token 时抛出 `EXTERNAL_SSO_DISABLED` / `EXTERNAL_SSO_TOKEN_MISSING`。
- **外部用户获取**
- 默认客户端会在 `external-sso.remote` 中加载请求配置,支持 GET/POST 等多种场景。
- 占位符:`${externalUserId}`(初始值为 token、`${shareToken}`/`${token}`(共享服务访问 token、`${xToken}`(原始回调 token、`${targetUri}`、`${sourceSystem}`。
- 会自动通过 `ShareServiceUtils` 获取共享服务访问 token并写入 `ShareServiceProperties#tokenHeaderName` 对应的请求头。
- 请求体会以 JSON 形式发送 `{ "x-token": "回调参数中的 token" }`,满足上游接口 `S_BF_CS_01` 的要求。
- `validateResponse` 可按 `codeField` 与 `successCode` 校验业务状态,失败时抛出带详细信息的 `ExternalSsoClientException`。
- **本地账号匹配**
- 使用 `mapping.order` 控制字段优先级;`custom.entries` 保存 "外部ID → 本地用户ID" 静态映射。
- 找不到用户或账号禁用时分别抛出 `EXTERNAL_SSO_USER_NOT_FOUND`、`EXTERNAL_SSO_USER_DISABLED`,均会同步写入登录日志。
- **令牌签发与日志**
- 通过 `OAuth2TokenService#createAccessToken` 使用默认客户端 `CLIENT_ID_DEFAULT` 颁发本地访问令牌。
- `recordAuditLog` 把原始响应的 SHA-256 摘要、外部属性、token 摘要等写入操作日志,便于排查。
- `recordLoginLog` 记录登录行为并在成功时更新用户最后登录 IP。
## `ExternalSsoClient` 扩展
- 默认实现 `DefaultExternalSsoClient` 由 `ExternalSsoClientConfiguration` 自动注册,若需要接入其它协议,可在任意配置类中自定义:
```java
@Bean
public ExternalSsoClient customExternalSsoClient(...) {
return new MyExternalSsoClient(...);
}
```
- 默认实现要点:
- 按配置构造 `RestTemplate`,支持连接/读取超时、HTTP 代理、重试次数。
- 解析 JSON 响应,将指定字段映射到 `ExternalSsoUserInfo`,并保留原始 data 节点到 `attributes`。
- 在解析失败、状态码异常时抛出带原始响应的 `ExternalSsoClientException`。
## 配置项参考
```yaml
external-sso:
enabled: true
system-code: example-partner
token:
secret: "shared-secret"
algorithm: AES
allowed-clock-skew-seconds: 60
max-age-seconds: 300
require-nonce: false
replay-protection-enabled: false
remote:
base-url: http://10.1.7.110
user-info-path: /api/sso/user
method: POST
headers:
Authorization: "Bearer ${token}"
query-params: {}
body:
userId: "${externalUserId}"
code-field: code
success-code: "0"
message-field: message
data-field: data
user-id-field: data.userId
username-field: data.username
nickname-field: data.nickname
email-field: data.email
mobile-field: data.mobile
tenant-id-field: data.tenantId
connect-timeout-millis: 3000
read-timeout-millis: 5000
retry-count: 1
proxy:
enabled: false
mapping:
order:
- EXTERNAL_ID
- USERNAME
- MOBILE
ignore-case: true
update-profile-on-login: false
custom:
entries:
partnerUser001: 10001
cors:
allowed-origins:
- https://partner.example.com
allowed-methods: ["OPTIONS", "POST"]
allowed-headers: ["Authorization", "Content-Type"]
allow-credentials: true
max-age: 1800
```
| 配置路径 | 说明 |
| --- | --- |
| `enabled` | 总开关,关闭后接口直接返回 `EXTERNAL_SSO_DISABLED` |
| `system-code` | 默认来源系统标识,可作为 `sourceSystem` 的缺省值及日志标签 |
| `token.*` | 若仍需解密/校验外部 token可在自定义 `ExternalSsoClient` 内按需使用;默认实现仅透传 |
| `remote.*` | 外部接口 HTTP 调用参数、字段映射与超时控制,模板占位符支持 `externalUserId`、`shareToken`(`token`)、`xToken`、`targetUri`、`sourceSystem` |
| `mapping.order` | 本地账号匹配优先级,支持 `EXTERNAL_ID`、`USERNAME`、`MOBILE` |
| `mapping.custom.entries` | 外部用户标识到本地用户 ID 的静态映射表 |
| `cors.*` | 用于开放 `/system/sso/verify` 的跨域访问白名单 |
## 错误码与日志
- 错误码:
- `1_002_000_050`:功能未开启。
- `1_002_000_051`token 缺失。
- `1_002_000_055`:外部接口异常,具体原因写入占位符。
- `1_002_000_056`:未匹配到本地用户。
- `1_002_000_057`:本地用户已禁用。
- `1_002_000_058`:来源系统不支持,需配置匹配的策略实现。
- 登录日志:使用 `LoginLogTypeEnum.LOGIN_EXTERNAL_SSO` 记录成功/失败。
- 操作日志:类型 `EXTERNAL_SSO/VERIFY`,包含外部用户 ID、映射账号、目标地址、来源系统、响应摘要等元数据。
## 注意事项
- 所有账号须提前维护映射,系统不会自动创建或按邮箱兜底匹配用户。
- `targetUri` 会在前端归一化,避免开放跳转风险;无合法目标时默认跳转首页。
- 确保外部接口返回的 JSON 字段与配置保持一致,必要时可通过 `remote.data-field` 指向具体节点。
- 若外部接口速度较慢或易失败,可提高 `retry-count`、超时时间或自定义客户端实现。
- 如需记录更多审计信息,可在 `ExternalSsoUserInfo#addAttribute` 注入自定义字段,审计日志会自动保留。
## 扩展与测试建议
- 通过提供新的 `ExternalSsoStrategy` 或 `ExternalSsoClient` Bean可扩展不同来源系统的对接方式。
- 建议为主要错误场景编写集成测试token 缺失、映射缺失、来源系统不支持、外部接口超时、账号被禁用等。
- 外部系统回调前可先调用 `/system/sso/verify` 联调接口验证配置是否正确,再接入正式流程。