Skip to content

04 — SSO 接入(门户作 IdP)

第三方应用信任门户签发的身份,实现单点登录。本文为实用端点清单;完整方案、安全与分期见 doc/07-单点登录方案.md

前提:用户须已在门户 sys_user 开户;Client 已在 sys_sso_client 注册(见 01-接入准备.md)。


1. 协议选型

协议门户角色Client protocol典型用途
OAuth 2.0授权服务端OAUTH2(默认)授权码换 opaque access_token,调 userinfo / 开放 API
OIDC同上 + Discovery / JWKS / id_tokenOIDC需要标准 OIDC 客户端、JWT id_token(RS256)
CAS 3.0CAS ServerCAS传统 CAS Client;须部署授权功能码 sso.cas
SAML 2.0IdPSAMLSP 对接 AD FS 等;配置 entity_id / acs_urls

access_tokenSa-Token opaque(存 Redis),不是 JWT。OIDC 时仅额外的 id_token 为 JWT。

不支持grant_type=client_credentials(Discovery grant_types_supported 仅为 authorization_coderefresh_token)。


2. OAuth2 / OIDC 端点

方法路径说明
GET/.well-known/openid-configurationOIDC Discovery(issuer、authorize/token/userinfo/jwks、scopes 等)
GET/oauth2/jwksJWKS 公钥(验 id_token
GET/oauth2/authorize授权码入口;须已登录门户会话
POST/oauth2/doConfirm用户同意授权(consent_mode 需要时)
POST/oauth2/tokencode 换 token(application/x-www-form-urlencoded
POST/oauth2/refresh刷新 access_token
POST/oauth2/revoke回收 token
GET/oauth2/userinfo用户信息;Token 须含 profile
POST/oauth2/introspectToken 自省;含 permissions 时返回权限快照

授权码流程(摘要)

  1. 浏览器访问
    GET /oauth2/authorize?response_type=code&client_id=...&redirect_uri=...&scope=...&state=...
    (OIDC 建议带 nonce;scope 含 openid
  2. 未登录 → 302 {issuer}/auth/login?redirect={相对路径 /oauth2/authorize?...},登录成功后前端须整页跳回该 redirect 以继续发码并回第三方;需确认 → /oauth2/consent.html
  3. 成功 → 302 redirect_uri?code=...&state=...(Location 会对 redirect_uri 的 query 值做 percent-encode,避免值中含 # 时被浏览器截成 fragment 丢 code
  4. 服务端 POST /oauth2/token
    grant_type=authorization_code&code=...&redirect_uri=...&client_id=...&client_secret=...
    (或 Basic:Authorization: Basic base64(client_id:client_secret)
  5. redirect_uri 须与 authorize 时完全一致(含 query,换票要用同一串);白名单校验忽略 query/fragment,只比对 scheme+host+path(登记 https://app.example.com/r/ov 即可匹配带参回调)。须同时落在 Client 授权域内。

Scope(开放 API 相关)

scope可访问
profile/oauth2/userinfo
permissionsintrospect 扩展 roleCodes / permissions
api:read/api/open/** 只读(当前主要为任务查询)
api:write/api/open/** 写(任务创建等;蕴含读)
openidOIDC:Token 响应含 JWT id_token

OAuth2 Token 禁止访问 /api/system/* 等门户业务管理 API。任务中心细节见 03-任务中心-OAuth2.md;设计表见 doc/07 §4.6。

Token 换票 curl 示意

bash
curl -sS -X POST "https://portal.example.com/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=AUTHORIZATION_CODE" \
  -d "redirect_uri=https://app.example.com/oauth/callback" \
  -d "client_id=demo-client" \
  -d "client_secret=YOUR_SECRET"

3. CAS 端点

方法路径说明
GET/POST/cas/login?service={url}登录并签发 ST,302 至 service?ticket=ST-...
GET/POST/cas/logout销毁门户会话后 302 登录页并带 redirect=/cas/login?service=...service/url 参数或登录时 Cookie);第三方应传与 login 相同的 service
GET/api/auth/cas-resume公开;SPA 在 redirect=/ 时按 Cookie 恢复续跳
GET/cas/p3/serviceValidate?service=...&ticket=...校验 ST,返回 CAS XML(一次性)

Client 侧登记允许的 service URL(protocol_config.service_urlsredirect_uris 均计入白名单;根路径有无尾 / 等价)。未授权功能码时 validate 返回 LICENSE 失败 XML。

管理端编辑 CAS Client 时须同步更新两处;仅改界面「服务地址」而库内旧 service_urls 不一致时,旧版会报「未登记的 service URL」。


4. SAML IdP 端点

方法路径说明
GET/saml/idp/metadataIdP 元数据 XML
GET/POST/saml/idp/ssoSSO(携带 SAMLRequest)
GET/POST/saml/idp/slo单点登出

Client protocol=SAML 时配置 entity_idacs_urls 等(见创建 Client 请求字段)。默认 EntityID 形态:{issuer}/saml/idp


5. 与开放 API / 出向的关系

能力是否用本 Token
/api/open/tasks/**是(需 api:* scope)
/api/open/matters/**(HMAC,见 02
接收组织用户出向推送无 Token;你方暴露 HTTP,见 05

管理端配置 Client / 出向:/api/sso/clients/api/sync/** 仍属门户运维面,第三方不直接调用。


6. 对接检查清单

  1. Client:redirect_uris / 授权域 / scopes / protocol 正确;生产 HTTPS。
  2. OIDC:拉取 Discovery 与 JWKS;校验 id_tokeniss/aud/nonce
  3. CAS:service URL 与 validate 时一致。
  4. SAML:交换元数据,ACS 与门户配置一致。
  5. 需要调任务开放 API:authorize 时申请 api:write(或 api:read)。

细节与分期以 doc/07-单点登录方案.md 为准;若与本文冲突,以代码与 Knife4j 为准,并提请更新本目录。

小程序 web-view 与业务域名

终端用户从微信小程序打开贵方 H5 时:门户先在小程序内换一次性票,经门户落地页为 web-view 种与 PC 相同的门户会话 Cookie,再进入贵方入口 URL;之后访问 /oauth2/authorize(或 CAS/SAML)与浏览器 PC 流程相同,第三方无新协议、无新回调字段。会话桥见 doc/54-小程序H5会话桥.md

打开只有这一条。 不要让门户把长期 Token 拼进 URL;不要在 H5 里放 client_secret

微信侧(PC 没有、小程序必做):

说明
业务域名公众平台 → 开发管理 → 业务域名。须逐条加:门户 issuer 主机(落地页 /auth/mp-webview-bridge)+ 每一个 H5 主机。不支持通配符。
校验文件微信下发 MP_verify_….txt,放到该主机站点根。门户自有 Nginx 已 include default.d 的站点(如 smart.*nyxg.demo.*)可走仓库 deploy/nginx/default.d/site-verify.conf不在门户 Nginx 上的业务系统须自己放文件
开发 vs 真机开发者工具可勾「不校验合法域名」;体验版 / 正式版不行。

门户应用移动地址应配贵方 SSO 入口(例如 /pub/sso/smart/auth?redirect=...),小程序会话桥校验 next 与登记 appUrl 同 origin

样板:能源 H5(ny-app)

能源后端已按 WeCard / KIM 同构接本门户(ny-java GET /pub/sso/smart/auth)。H5 仍只读能源自己的 access_tokenLoginByToken),不改 OAuth 协议。

  1. 管理端为 H5 SP 登记 OAuth2 回调(可在现有能源 Client 上「添加回调地址」,或新建 Client):protocol=OAUTH2redirect_urishttps://nyxg.demo.hechuanghuixin.com/pub/sso/smart/callbackallowedDomainsnyxg.demo.hechuanghuixin.com不必加 H5 子域),scope 含 profile关闭强制 PKCE。PC 已有的 /pub/sso/portal/callback/... 不要改。
  2. 能源进程写入 SMART_SSO_CLIENT_ID / SMART_SSO_CLIENT_SECRET / SMART_SSO_APP_URL=https://nyxg.demo.hechuanghuixin.com(及可选 SMART_SSO_INNER_BASE)。H5 独立子域时再配 SMART_SSO_ALLOWED_REDIRECT_HOSTS=nyxgapp.demo.hechuanghuixin.comSMART_SSO_H5_BASE=https://nyxgapp.demo.hechuanghuixin.com(代码默认已是这两项)。
  3. 门户应用:仅移动;appUrl =
    https://nyxg.demo.hechuanghuixin.com/pub/sso/smart/auth?redirect= + encodeURIComponent(https://nyxgapp.demo.hechuanghuixin.com/#/wel/index)
    会话桥 origin 是 nyxg.demo;落地页才是 nyxgapp.demo。种数脚本:smart-home-ui/scripts/seed-energy-patrol-mobile-apps.mjs
  4. 用户映射:门户 username = 能源 userCode;两边都要有这个人。
  5. 公众平台业务域名加上 smart.hechuanghuixin.comnyxg.demo.hechuanghuixin.com(SSO)与 nyxgapp.demo.hechuanghuixin.com(H5)。
  6. 验收:小程序正式用户点应用 → 会话桥 → nyxg.demo/pub/sso/smart/auth → 门户 authorize → 已登录的 ny-app(nyxgapp.demo)。PC 管理端仍走原 /pub/sso/portal/callback/...

换票须 POST /oauth2/tokenapplication/x-www-form-urlencoded),不要学部分旧 IdP 用 GET。userinfo 为 GET /oauth2/userinfo + Authorization: Bearer。能源 H5 为 hash 路由,回跳 redirect 须带 #/wel/index

细节与环境变量见能源仓 ny-java/README.md「融合门户 SSO」。


7. 下载 Demo