Files
zt-dsc/docs/外部单点登录.md
chenbowen 0b646295da 1. 新增 iwork 同步用户组织信息接口
2. 修复错误设置版本信息在 zt-dependencies 的 bug
2025-11-20 18:27:01 +08:00

10 KiB
Raw Blame History

外部单点登录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允许业务自定义覆盖。
  • ExternalSsoPropertiesexternal-sso.* 配置项,包含开关、外部接口、账号映射、跨域等子配置;示例配置已同步到 zt-module-system/zt-module-system-server/src/main/resources/application.yamlzt-server/src/main/resources/application.yaml
  • ExternalSsoVerifyReqVOPOST /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,请求体:

    {
      "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 缺失、外部接口失败、账号不存在)时,后端返回对应错误码,前端弹出提示并清除本地缓存。

下图为时序示意:

外部系统 -> 浏览器 -> Vue /externalsso -> POST /system/sso/verify -> ExternalSsoService -> ExternalSsoClient -> 外部用户接口
                                   \-> OAuth2TokenService -> 登录日志/审计日志

前端细节

  • ExternalSsoCallback.vuetarget/targetUri/redirect 参数做统一解码与归一化,支持携带绝对地址或 hash 路由,防止开放跳转漏洞。
  • 解析 URL 中的 sourceSystem(兼容 sourcesystemCode)并透传给后端,便于在多来源系统场景下选择策略。
  • 在成功回调后调用 router.replace,保证不会产生历史记录;失败时引导用户回到 /login 并附带原目标地址。
  • 通过 buildErrorMessage 兼容后端返回的 msgError 对象或字符串,统一展示错误提示。

后端流程拆解

  • 开关与参数校验
    • 关闭开关或缺少 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 可按 codeFieldsuccessCode 校验业务状态,失败时抛出带详细信息的 ExternalSsoClientException
  • 本地账号匹配
    • 使用 mapping.order 控制字段优先级;custom.entries 保存 "外部ID → 本地用户ID" 静态映射。
    • 找不到用户或账号禁用时分别抛出 EXTERNAL_SSO_USER_NOT_FOUNDEXTERNAL_SSO_USER_DISABLED,均会同步写入登录日志。
  • 令牌签发与日志
    • 通过 OAuth2TokenService#createAccessToken 使用默认客户端 CLIENT_ID_DEFAULT 颁发本地访问令牌。
    • recordAuditLog 把原始响应的 SHA-256 摘要、外部属性、token 摘要等写入操作日志,便于排查。
    • recordLoginLog 记录登录行为并在成功时更新用户最后登录 IP。

ExternalSsoClient 扩展

  • 默认实现 DefaultExternalSsoClientExternalSsoClientConfiguration 自动注册,若需要接入其它协议,可在任意配置类中自定义:

    @Bean
    public ExternalSsoClient customExternalSsoClient(...) {
        return new MyExternalSsoClient(...);
    }
    
  • 默认实现要点:

    • 按配置构造 RestTemplate,支持连接/读取超时、HTTP 代理、重试次数。
    • 解析 JSON 响应,将指定字段映射到 ExternalSsoUserInfo,并保留原始 data 节点到 attributes
    • 在解析失败、状态码异常时抛出带原始响应的 ExternalSsoClientException

配置项参考

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 调用参数、字段映射与超时控制,模板占位符支持 externalUserIdshareToken(token)、xTokentargetUrisourceSystem
mapping.order 本地账号匹配优先级,支持 EXTERNAL_IDUSERNAMEMOBILE
mapping.custom.entries 外部用户标识到本地用户 ID 的静态映射表
cors.* 用于开放 /system/sso/verify 的跨域访问白名单

错误码与日志

  • 错误码:
    • 1_002_000_050:功能未开启。
    • 1_002_000_051token 缺失。
    • 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 注入自定义字段,审计日志会自动保留。

扩展与测试建议

  • 通过提供新的 ExternalSsoStrategyExternalSsoClient Bean可扩展不同来源系统的对接方式。
  • 建议为主要错误场景编写集成测试token 缺失、映射缺失、来源系统不支持、外部接口超时、账号被禁用等。
  • 外部系统回调前可先调用 /system/sso/verify 联调接口验证配置是否正确,再接入正式流程。