跳到正文

配置 SSO 企业登录

SSO(Single Sign-On,单点登录)让你用企业身份系统(IdP,Identity Provider,身份提供方,如 Azure Entra / Okta)签发的 token 调用网关,或用企业账号登录控制台,而不必给每个人/每个程序单独发网关密钥。

入口:控制台 → 设置SSO 页签。配置存在数据库里,改完即时生效,不需要重启,也不需要任何环境变量。

SSO 在网关里做两件互相独立的事,可以只开其一,也可以同一 provider 两件都开:

  1. 数据面认证:你的程序持 IdP 发的 Bearer token 调 /v1/... 等代理接口;网关验签后,从 token 里取一个名字,去访问密钥表里找同名的真实密钥,用那把密钥的权限放行。
  2. 控制台登录:人在登录页点"用企业账号登录",跳去 IdP 认证,回来后网关从 id_token 取用户名,去控制台用户表里找同名用户,命中才让进。

核心原则:网关不凭空造身份。数据面要预先建好同名访问密钥,控制台要预先建好同名控制台用户;找不到一律拒绝(401),不会临时合成一个。

30 秒搞懂四个词

后面会反复出现,先认一下:

  • issuer(签发地址):token 里的 iss 字段,是 IdP 写的一句"这张 token 是我发的",是个网址。网关拿它对暗号——token 的 iss 必须等于你在这里填的 issuer,网关才肯认这张 token 归这个 provider 管。
  • audience(受众):token 里的 aud 字段,是"这张票开给哪个应用的"。网关拿它确认 token 是开给你这个网关的,不是开给别的应用的。
  • discovery 文档(IdP 的自我介绍):每个 IdP 都有一份公开的 JSON 自我介绍,里面写全了四件事——① 我是谁 issuer、② 我的公钥清单在哪 jwks_uri、③ 登录网址、④ 换 token 网址。网关优先读这份自我介绍来拿这些网址,而不是自己猜。
  • JWKS / 公钥清单:IdP 的公钥列表,网关用它验"token 没被人篡改"。

三种 provider,每个框填什么

页面上每个 provider 都有这几个核心框(三种类型通用):

填什么
ID给这个 provider 起的内部名字(如 corp-azure),URL 里会用到,建好别改。
Issuer URL(签发地址)按你填的原样使用,网关不会从域名/租户去拼。token 的 iss 须等于它(末尾斜杠带不带都行,网关比对时会自动归一)。
Audience(受众)须等于 token 的 aud。仅数据面用。
Client ID你在 IdP 注册的应用 ID。控制台登录用它换 token + 校验 id_token 受众;SPA 模式还用它做数据面的客户端精确匹配。
属性映射 name_expr数据面:从 token 的哪个 claim 取出"名字",去访问密钥表找同名密钥(JSONPath 或表达式,如 $.oid$.upn)。
控制台用户名 username_expr控制台登录:从 id_token 的哪个 claim 取用户名,去控制台用户表找同名用户。

类型(Type)选 azure / okta / generic:前两者网关知道它们的网址规律、能自动推导;generic 用于任何标准 OIDC,登录/换 token 网址要你手填。

高级折叠区里还有:jwks_uri(手填公钥地址,一般不用)、使用 OIDC 发现开关、Discovery URL(发现文档地址,可选)、时钟容差、公钥刷新间隔。下面 Azure 专节会讲 discovery_url 什么时候必须填。

Azure 专节(重点)

Azure 是最容易配错的一家,因为它的网址体系分裂、v1/v2 token 形态不同。按下面抄,基本一次过。

issuer 填什么

  • 全球 v2:去 Entra 概览页复制 Directory (tenant) ID(一串 GUID),填进模板 https://login.microsoftonline.com/<粘贴租户ID>/v2.0,整串就是 issuer。末尾 /v2.0 不能省,也别填 commonorganizations{tenantid}
  • v1 / 中国云 / 主权云:直接填 token 里的 iss(解 token 看 iss 字段,或按租户拼 https://sts.chinacloudapi.cn/<租户ID>)。这种 token 的 iss 末尾常带斜杠——没关系,网关比对会自动归一。

audience 何时带 api://

看你在 portal 里 Expose an API → Application ID URI 设成了什么:

  • 设成裸 GUID(默认)→ audience 填裸 GUID(与 Client ID 相同)。
  • 设成 api://<client_id> → token 的 aud 会带 api:// 前缀,这里也必须照填 api://<client_id>,不能只填裸 GUID,否则对不上 → 401。

discovery_url 去哪找(v1 / 中国云 / 主权云必填)

为什么需要它:v1 / 中国云 token 的 isssts.* 域名,但 IdP 的自我介绍(discovery 文档)在 login.* 域名——两个不同的网址。issuer 只用来对暗号,不能用来拼自我介绍地址(拼出来 404)。所以要单独告诉网关自我介绍在哪。全球 v2 因为 iss 和自我介绍同址,不用填,留空即可。

首选:portal 直接抄,零猜测。

  1. 进 Entra portal(中国云是 portal.azure.cn)→ 左侧 应用注册(App registrations) → 点你那个应用。
  2. 左侧菜单 终结点(Endpoints)
  3. 右侧面板里 "OpenID Connect 元数据文档" 那一行,整行复制填进 Discovery URL

这条通常是 v2 的(带 /v2.0),没关系,能用:同一应用同一租户的签名密钥 v1 和 v2 共用一组,所以 v2 自我介绍里的公钥照样验得了你的 v1 token。

备选:手拼模板{tid} 用 token 里的 tid,或 portal 概览页的 Directory (tenant) ID):

  • 中国云:https://login.partner.microsoftonline.cn/{tid}/v2.0/.well-known/openid-configuration
  • 全球云:https://login.microsoftonline.com/{tid}/v2.0/.well-known/openid-configuration

⚠️ 中国云的 login 域名是 login.partner.microsoftonline.cn不是 chinacloudapi.cn——后者是 Microsoft Graph 的域名,别混。以 portal『终结点』面板为准。

怎么确认拼对了:浏览器或 curl 打开这个 discovery_url,应返回一段 JSON 且含 jwks_uri;再打开里面的 jwks_uri,看得到公钥列表就稳了。

name_expr:把 claim 映射成访问密钥的名字

  • v1 access token 有 upn(如 alice@corp.example.com):若你的访问密钥 name 建的是 @ 前面的部分,用表达式削掉域名:{{ replace(claim:$.upn, "@.*$", "") }}alice
  • v2 access token 没有 upn:用 $.oid(用户对象 ID 全串),并把访问密钥的 name 也建成这串 OID。
  • 前提:访问密钥表里得有 name 等于解析结果的那把密钥,否则 401。

完整配置例子

例子 A:Azure 中国云 v1(带 discovery_url)

jsonc
{
  "id": "azure-cn",
  "type": "azure",
  "enabled": true,
  // 对暗号:逐字等于 token 的 iss(末尾斜杠带不带都行)。
  // 下面的租户/对象 ID 是示例占位符,请换成你自己在 portal 概览页
  // 复制的 Directory (tenant) ID / Application (client) ID。
  "issuer": "https://sts.chinacloudapi.cn/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  // 票开给谁:逐字等于 token 的 aud。这里带 api:// 是因为 portal 把
  // Application ID URI 设成了 api://<client_id>。
  "audience": "api://11111111-2222-3333-4444-555555555555",
  "client_id": "11111111-2222-3333-4444-555555555555",
  // 数据面:upn 削掉 @ 后面 → alice(访问密钥表须有 name=alice)。
  "access_key": {
    "enabled": true,
    "name_expr": "{{ replace(claim:$.upn, \"@.*$\", \"\") }}"
  },
  // ★ 自我介绍地址:抄 portal『终结点』面板 "OpenID Connect 元数据文档" 那一行。
  // 公钥地址由它自动给出,不用再手填 jwks_uri。
  "discovery_url": "https://login.partner.microsoftonline.cn/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/v2.0/.well-known/openid-configuration"
}

例子 B:全球 v2(不填 discovery_url,老配置零改动)

jsonc
{
  "id": "azure-global",
  "type": "azure",
  "enabled": true,
  "issuer": "https://login.microsoftonline.com/<你的租户ID>/v2.0",
  "audience": "<你的 aud>",
  "client_id": "<你的 client id>",
  "access_key": { "enabled": true, "name_expr": "$.oid" }
  // 没有 discovery_url → 网关按 issuer 自动拼自我介绍地址(v2 拼得对)。
}

多个 provider 怎么工作

你可以同时配 Azure + Okta + 通用 OIDC 多家。网关不会拿你的 token 去逐家验签:它先解出 token 的 iss,对暗号找到唯一认领的 provider,只验一次签。各家 issuer 互不相同,所以一个 token 至多被一家认领。

没被任何 provider 认领的 token(不是企业 IdP 发的 JWT、或普通 sk-... 密钥)会回落到网关内置的访问密钥认证——SSO 不会误拒你原来的密钥。

常见 401 排查表

现象 / 怀疑点怎么查 / 怎么改
token 是 v1 / 中国云,issuer 对不上issuer 填 token 的 iss 原样;末尾斜杠不用纠结。若 iss 域名是 sts.*必须另填 discovery_url(login 域名)。
audience 对不上看 portal『Expose an API → Application ID URI』:是 api://<id> 就带 api:// 填,是裸 GUID 就填裸 GUID。
验签失败 / 拉不到公钥discovery_url 域名对不对(中国云是 login.partner.microsoftonline.cn,不是 chinacloudapi.cn);用浏览器打开它应返回含 jwks_uri 的 JSON。
签名验过但仍 401name_expr 取不到值,或取出的名字在访问密钥表里没有同名密钥。v2 access token 没有 upn/azp 时别用它们,改 $.oid
控制台登录回不来 / 报 not_in_listid_token 的 username_expr 取出的用户名,在控制台用户表里没有同名且启用的用户
token 过期解 token 看 exp;过期就重新获取 token。
配了 SSO 但 token 像没走 SSO确认该 provider enabledaccess_key.enabled 都开了;确认 SSO 插件已随镜像内置启用(官方镜像默认启用)。

下一步