主数据平台 1.5.1 接入实践:从 Ticket 单点登录到 OAuth2 授权码流程

2026-09-17 32 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

主数据平台 1.5.1 的文档更新,重点落在应用接入和后端源码分析上。新版接入说明覆盖 Ticket 模式与 OAuth2 授权码模式,并补充了若依系统的接入示例、OAuth2 客户端支持说明,以及授权码、Token 和 state 参数之间的区别。

对开发团队来说,这类文档升级的价值不只是“多了几页说明”,而是把单点登录从概念变成了可以核对、调试和落地的流程。下面用一套可改造的示例,梳理两种协议的边界与工程实践。

两种登录模式,解决不同问题

Ticket 模式通常适合已有统一认证中心、并且希望通过一次性票据完成登录的系统。业务系统收到 Ticket 后,需要通过后端接口向认证中心校验,确认 Ticket 没有被使用过、没有过期,并取得用户身份信息。

OAuth2 授权码模式则更适合标准化的第三方授权场景。浏览器先被重定向到授权端,用户完成认证后,授权端通过回调地址返回一次性 code。业务后端再使用客户端凭证交换 Access Token,最后调用用户信息接口。

两者都可能出现“浏览器跳转”和“回调”,但安全边界不同:

  • Ticket 是认证中心签发给业务系统的一次性登录凭证,通常需要服务端校验。
  • OAuth2 code 是授权码,不是 Access Token,也不能直接当作用户身份令牌。
  • OAuth2 state 用来关联发起登录的会话并防止 CSRF,不能拿来替代 code 或 Token。
  • Access Token 应保存在服务端会话、受保护的缓存或密钥管理系统中,不应无理由暴露给浏览器。

OAuth2 授权码流程的可运行示例

下面的 Shell 示例演示完整的 Token 交换过程。示例中的域名、客户端 ID 和授权码都是占位值,接入时替换为平台文档提供的实际配置。命令依赖 curljq

#!/usr/bin/env bash
set -euo pipefail

AUTH_BASE="https://sso.example.internal"
CLIENT_ID="replace-with-client-id"
CLIENT_SECRET="replace-with-client-secret"
REDIRECT_URI="https://app.example.internal/sso/callback"
CODE="replace-with-one-time-authorization-code"

TOKEN_JSON="$(curl --fail-with-body -sS -X POST "$AUTH_BASE/oauth/token" \\
  -H 'Content-Type: application/x-www-form-urlencoded' \\
  --data-urlencode "grant_type=authorization_code" \\
  --data-urlencode "client_id=$CLIENT_ID" \\
  --data-urlencode "client_secret=$CLIENT_SECRET" \\
  --data-urlencode "redirect_uri=$REDIRECT_URI" \\
  --data-urlencode "code=$CODE")"

ACCESS_TOKEN="$(jq -r '.access_token // empty' <<< "$TOKEN_JSON")"
if [[ -z "$ACCESS_TOKEN" ]]; then
  echo "Token exchange failed: $TOKEN_JSON" >&2
  exit 1
fi

curl --fail-with-body -sS "$AUTH_BASE/api/userinfo" \\
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

实际实现中,回调接口还需要校验以下内容:

  1. state 是否与服务端保存的登录会话一致。
  2. redirect_uri 是否与发起授权时完全一致。
  3. code 是否已经使用或已经过期。
  4. Token 响应中的 token_type、过期时间和权限范围是否符合预期。
  5. 用户信息接口返回的唯一标识是否能稳定映射到本地账号。

不要把 client_secret 放到前端 JavaScript、移动端包或公开配置文件中。授权码交换应由后端完成,并通过 HTTPS 传输。

Ticket 模式的服务端校验

Ticket 模式可以抽象为三个请求:业务系统创建登录入口,认证中心回调 Ticket,业务后端向认证中心验证 Ticket。一个最小化的伪代码如下,接口路径和字段名需要按平台实际文档调整:

from flask import Flask, request, redirect, session, jsonify
import requests

app = Flask(__name__)
app.secret_key = "replace-in-production"

SSO_VERIFY_URL = "https://sso.example.internal/api/ticket/verify"
APP_HOME = "https://app.example.internal/"

@app.get("/sso/callback")
def sso_callback():
    ticket = request.args.get("ticket")
    if not ticket:
        return jsonify(error="missing ticket"), 400

    response = requests.post(
        SSO_VERIFY_URL,
        json={"ticket": ticket},
        timeout=5,
    )
    response.raise_for_status()
    payload = response.json()

    if not payload.get("valid"):
        return jsonify(error="invalid or expired ticket"), 401

    user_id = payload.get("user_id")
    if not user_id:
        return jsonify(error="missing user identity"), 502

    session["user_id"] = user_id
    return redirect(APP_HOME)

这段代码只是接入骨架。生产环境还应加入 Ticket 重放防护、请求日志脱敏、认证中心超时处理、用户自动开户策略,以及本地会话过期策略。尤其要避免在日志中打印完整 Ticket、客户端密钥和 Access Token。

若依系统接入时的几个检查点

若依类后台系统通常已经具备用户、角色、菜单和会话管理,因此接入重点不是重新实现整套认证,而是把外部身份映射到本地用户体系。

可以按下面的顺序验证:

  • 明确外部用户唯一标识,例如工号、用户 ID 或统一身份中心的 sub
  • 确认首次登录时是自动创建本地用户,还是只允许预先存在的账号登录。
  • 将外部角色映射到若依角色时,定义“覆盖”还是“追加”规则。
  • 将认证中心退出与本地会话注销行为分开测试。
  • 分别测试登录成功、用户取消授权、授权码过期、Ticket 重复使用、回调地址不匹配和用户不存在等分支。

工程团队还可以把接入配置集中管理,避免散落在控制器代码中:

sso:
  enabled: true
  mode: oauth2
  authorize-url: https://sso.example.internal/oauth/authorize
  token-url: https://sso.example.internal/oauth/token
  userinfo-url: https://sso.example.internal/api/userinfo
  client-id: ${SSO_CLIENT_ID}
  client-secret: ${SSO_CLIENT_SECRET}
  redirect-uri: https://app.example.internal/sso/callback
  scopes:
    - openid
    - profile

其中 client-secret 应通过环境变量、密钥管理服务或部署平台的 Secret 注入。配置文件可以提交到代码仓库,但敏感值不应直接提交。

用文档和源码分析减少接入成本

新增流程图、时序图和后端源码分析文档,对排查“浏览器已经跳回来了但仍然登录失败”这类问题尤其有帮助。阅读时建议沿着三条线索核对:

  1. 请求线:浏览器跳转到哪里,回调地址是什么,参数在哪一跳产生。
  2. 凭证线:Ticket、授权码、Access Token 分别由谁签发、在哪里校验、能否重复使用。
  3. 账号线:外部用户标识如何转换为本地用户,角色和权限在哪一步加载。

如果团队使用 Claude Code 或其他代码助手辅助源码分析,可以把问题拆成可验证的小任务,例如:

请分析登录回调的调用链,不要修改代码。输出:
1. 回调入口文件和方法
2. state、code、access_token 的产生与校验位置
3. 外部用户 ID 映射到本地用户的字段
4. 可能导致重复登录或 Token 泄露的风险
5. 每个结论对应的文件路径和代码行号

这种提示方式把分析范围、输出格式和安全关注点写清楚,便于人工复核,也适合在团队中形成稳定的工程习惯。自动化工具可以帮助定位调用链,但认证协议的最终判断仍需要开发者结合配置、日志和实际请求验证。

上线前检查清单

  • 生产环境全链路使用 HTTPS。
  • state 使用不可预测的随机值,并绑定服务端会话。
  • 授权码只在后端交换,且验证 redirect_uri
  • Ticket 和授权码都设置一次性使用与过期控制。
  • Token、客户端密钥和用户敏感信息不写入普通日志。
  • 本地账号映射、角色同步和退出登录行为都有明确规则。
  • 对认证中心不可用、超时和返回异常建立降级或错误提示。
  • 在测试环境覆盖成功、取消、过期、重放、越权和账号不存在场景。

主数据平台 1.5.1 的接入文档升级,真正值得落地的部分是把协议差异、系统边界和调试路径讲清楚。团队可以先用一个低权限测试应用完成完整流程,再接入若依等业务系统,最后补齐日志审计、角色同步和异常恢复。这样既能快速验证协议,也能把上线风险控制在可观测范围内。


相关推荐