配置 SSO 企业登录
SSO(Single Sign-On,单点登录)让你用企业身份系统(IdP,Identity Provider,身份提供方,如 Azure Entra / Okta)签发的 token 调用网关,或用企业账号登录控制台,而不必给每个人/每个程序单独发网关密钥。
入口:控制台 → 设置 → SSO 页签。配置存在数据库里,改完即时生效,不需要重启,也不需要任何环境变量。
SSO 在网关里做两件互相独立的事,可以只开其一,也可以同一 provider 两件都开:
- 数据面认证:你的程序持 IdP 发的 Bearer token 调
/v1/...等代理接口;网关验签后,从 token 里取一个名字,去访问密钥表里找同名的真实密钥,用那把密钥的权限放行。 - 控制台登录:人在登录页点"用企业账号登录",跳去 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不能省,也别填common、organizations或{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 的 iss 在 sts.* 域名,但 IdP 的自我介绍(discovery 文档)在 login.* 域名——两个不同的网址。issuer 只用来对暗号,不能用来拼自我介绍地址(拼出来 404)。所以要单独告诉网关自我介绍在哪。全球 v2 因为 iss 和自我介绍同址,不用填,留空即可。
首选:portal 直接抄,零猜测。
- 进 Entra portal(中国云是
portal.azure.cn)→ 左侧 应用注册(App registrations) → 点你那个应用。 - 左侧菜单 终结点(Endpoints)。
- 右侧面板里 "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)
{
"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,老配置零改动)
{
"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。 |
| 签名验过但仍 401 | name_expr 取不到值,或取出的名字在访问密钥表里没有同名密钥。v2 access token 没有 upn/azp 时别用它们,改 $.oid。 |
| 控制台登录回不来 / 报 not_in_list | id_token 的 username_expr 取出的用户名,在控制台用户表里没有同名且启用的用户。 |
| token 过期 | 解 token 看 exp;过期就重新获取 token。 |
| 配了 SSO 但 token 像没走 SSO | 确认该 provider enabled 与 access_key.enabled 都开了;确认 SSO 插件已随镜像内置启用(官方镜像默认启用)。 |
下一步
- 控制台登录与角色 — 建控制台用户、理解角色与会话。
- 访问密钥与密钥组字段 — 建数据面要用的同名访问密钥。
- 端点与认证 — 认证顺序:SSO 与内置密钥如何衔接。
