API 网关专栏 · 第 87 章

API 网关与 CIAM 集成:Token 验证、同意与身份上下文

2026年09月21日
API 网关与 CIAM 集成:Token 验证、同意与身份上下文

客户身份与访问管理(CIAM)系统负责认证客户、管理身份生命周期与同意交互,并签发 Token。API 网关可以验证访问 Token 并执行粗粒度路由策略;应用仍需判断该主体在当前状态下是否能操作目标账户、订单、文档或工作流。

成功集成的关键是保留这条证据链。不要把任意 sub、邮箱、组或转发请求头合并成通用“可信用户”字段,也不能仅因为 OpenID Connect ID Token 同样采用 JWT,就把它当作访问 Token 接受。

关键要点

  • 面向用户的客户端应采用符合当前安全实践的授权码流程,绝不能在 API 客户端收集客户密码。
  • 针对具体 API 验证 Token 类型、签发者、签名、受众、时间、客户端和所需权限。
  • 在本地 JWT 验证和 Token 内省之间明确选择,并定义新鲜度、撤销、缓存、超时与故障行为。
  • 把 Scope、角色、组、权益和同意记录视为不同策略输入。
  • 只在经过认证且无法绕过的链路上传递上游所需的身份上下文。
  • 对资源和工作流的授权仍由应用负责。

把每项决策交给正确组件

1sequenceDiagram
2    participant U as 客户
3    participant C as 客户端应用
4    participant I as CIAM 授权服务器
5    participant G as API 网关
6    participant A as 应用与资源
7    U->>C: 开始登录或同意流程
8    C->>I: 携带资源、Scope 与 PKCE S256 code_challenge 的授权请求
9    I->>U: 认证并获取所需同意
10    I-->>C: 授权码
11    C->>I: 使用 PKCE code_verifier 及适用的客户端认证兑换授权码
12    I-->>C: 访问 Token 和可选 ID Token
13    C->>G: 携带访问 Token 的 API 请求
14    G->>G: 验证 Token 与粗粒度路由策略
15    G->>A: 请求与最小化已验证上下文
16    A->>A: 检查账户对象工作流与当前同意
17    A-->>G: 业务响应或拒绝
18    G-->>C: API 响应

该图展示逻辑职责流。公有客户端使用 PKCE,不能依赖共享客户端密钥;机密客户端在要求时进行客户端认证,也应使用 PKCE。RFC 9700 要求公有客户端使用 PKCE,建议机密客户端也使用,并指出 S256 是目前不会在授权请求中暴露 code_verifier 的挑战方法。授权服务器必须验证 code_verifier 并防止 PKCE 降级。JWT 验证可以使用本地缓存密钥;不透明 Token 验证可能调用内省端点;同意可能在授权前或授权过程中获取。应记录实际采用的路径,而不是假设每个 API 请求都同步调用 CIAM。

区分 ID Token 与访问 Token

OpenID Connect 在 OAuth 2.0 之上提供认证。ID Token向客户端传递认证信息,并以该客户端为受众;访问 Token则提交给 API 等资源服务器。

使用相同 JWT 语法并不意味着两种 Token 可以互换。Token 的类型、签发者、受众或预期用途与受保护 API 不匹配时,网关应拒绝它。把 ID Token 转发给每个后端,还会泄露这些服务并不需要的客户声明。

RFC 9700规定不得使用资源所有者密码凭据授权。面向客户的应用不应要求用户在应用或低代码工作流中输入 CIAM 密码。应使用适当的重定向授权流程,并根据客户端类型保护授权码和刷新 Token。

为每个 API 定义 Token 合同

对于遵循 RFC 9068 的 JWT 访问 Token 部署,资源服务器需要验证必需声明和密码学属性,包括 Token 类型、精确签发者、签名与允许算法、目标受众和过期时间。该规范要求包含 iss、exp、aud、sub、client_id 和 iat 等声明;未声明遵循该规范的部署可能采用不同合同。

一组说明性的解码声明如下:

1{
2  "iss": "https://login.example.com/",
3  "sub": "cust_2f8a1",
4  "aud": "https://api.example.com/orders",
5  "exp": 1792464000,
6  "iat": 1792460400,
7  "jti": "at_01K7YB1F",
8  "client_id": "mobile-orders",
9  "scope": "orders.read orders.cancel",
10  "tenant_id": "org_73a2"
11}

这是解码后的内容,不是验证成功的证明。必须检查签名、签发者元数据、密钥选择、算法策略、当前时间、受众和所需权限。tenant_id 是应用特定声明,必须定义其语义和签发者归属;它存在并不能证明主体可以访问该租户中的所有对象。

如果隐私模型要求降低跨服务关联,应使用成对或资源特定的主体标识符。RFC 9068 还提醒:资源服务器只应接收它有权获得的声明,包括基于同意或组织策略的声明。

有意识地选择 JWT 验证或内省

维度本地 JWT 验证Token 内省
请求时依赖可以缓存签发者密钥和元数据通常依赖授权服务器响应或缓存
撤销新鲜度通常受 Token 生命周期与其他撤销设计约束可反映当前服务器状态,但仍受缓存影响
Token 信息暴露声明对持有者和资源服务器可见不透明 Token 可把声明留在服务端
故障行为策略允许时,可继续用已有缓存密钥验证CIAM 不可用时调用可能失败或超时
隐私与大小声明随每个 Token 传输可针对受保护资源最小化响应

RFC 7662定义 Token 内省及其必需的 active 布尔值。active: true 表示授权服务器按自身规则认为 Token 处于活跃状态;网关仍须执行目标受众与权限检查,应用也仍负责资源授权。对于 active: false 的已授权响应,通常不会说明 Token 为何无效。超时或响应格式错误不等于 active: false;应定义失败关闭响应、有界缓存、指标和事件路径,而不是虚构活跃状态。

如果缓存内省响应,撤销和权益变更可能要等缓存项过期后才生效;如果本地验证 JWT,缩短 Token 生命周期可以减少但不能消除新鲜度取舍。应按风险与可用性选择合同,而不是追逐 Token 格式潮流。

在网关执行受众与权限检查

RFC 9700 建议对访问 Token 限制受众和权限。为个人资料 API 签发的 Token 不能仅因签发者和签名有效,就被重放到订单 API。

每条路由都应定义:

  • 可接受的签发者或信任域;
  • 访问 Token 类型和允许算法;
  • 精确的 API 受众或资源指示符;
  • 必需 Scope 或授权细节;
  • Token 代表用户、客户端,还是两者;
  • 适用时的发送方约束证明;
  • Token 和元数据缓存的最大年龄;
  • 时钟偏差策略;
  • 拒绝状态和安全错误细节。

Apache APISIX 3.19.0 的 openid-connect 插件可以强制要求 Bearer Token 和 Scope,并验证所配置的签发者与受众。零信任网关指南提供了固定版本的配置,并说明其中改变行为的布尔参数默认值和两条路径。应复用一份已审阅身份策略,而不是在各路由复制细微不同的配置。

分开管理同意、Scope 与授权

  • 同意记录客户针对特定用途、客户端、资源、数据类别和时间所同意的事项。
  • Scope 是为 Token 受众解释的权限字符串。
  • 角色或组 是一种分配,其权威性与生命周期取决于签发方。
  • 权益表示由产品、合同、账户或策略授予的访问权限。
  • 应用授权判断当前主体此刻能否对该资源执行该操作。

orders.read 等 Scope 可以允许某类路由,但应用仍要检查订单是否属于正确客户或受委托组织。同意撤回可能需要更新 CIAM、撤销或等待 Token 过期,并在应用侧执行。长期 Token 缓存不能静默延长同意决策,使其超过已定义的新鲜度。

不要把每项持续变化的业务权限都塞进大型 Token。随着 Token 积累账户状态,陈旧声明、请求头大小、隐私暴露和跨服务耦合都会增加。

传播最小化的已验证上下文

验证后,网关可以传递标准化主体、客户端、租户、Scope 集合、认证上下文或策略决定。必须保护这条链路:

  1. 移除调用方提供的内部身份请求头副本;
  2. 只添加从已验证 Token 或批准策略服务派生的值;
  3. 认证并加密网关到上游的流量;
  4. 阻止绕过清理与验证路径的后端直连;
  5. 让应用区分用户主体和客户端身份;
  6. 除非上游必须验证或使用,否则不要转发原始 Token 或完整声明集。

内部请求头只有在这条完整边界内才可信。一个请求头名称本身并不是身份协议。

测试生命周期与失败路径

不要只测试正常路径:

测试预期证据
错误签发者或受众网关在调用上游前拒绝
过期、尚未生效、格式错误或类型错误的 Token返回安全 Token 错误,不泄露声明
缺少所需 Scope路由拒绝,不调用应用
路由 Scope 有效但账户对象错误应用拒绝
已撤销不透明 Token内省返回 inactive,缓存行为符合合同
CIAM 超时执行已定义失败或有界缓存决策,不虚构 active 状态
签名密钥轮换受控刷新后接受新密钥,未知密钥安全失败
同意撤回在已记录的 Token/缓存/应用窗口内生效
凭证或客户端禁用阻止新授权,并验证已有 Token 策略
尝试直连后端网络或应用拒绝绕过

在隐私控制下记录签发者、受众决定、客户端、主体引用、策略原因、Token 标识符哈希或安全引用、配置版本和关联 ID。不要记录访问 Token 或不必要的客户声明。

CIAM 集成检查清单

  • 是否按用途与验证合同区分 ID Token 和访问 Token?
  • 是否检查签发者、签名、算法、受众、时间、客户端和权限?
  • 是否禁止资源所有者密码授权?
  • 是否明确 JWT 与内省的新鲜度、缓存、撤销、超时和故障行为?
  • 用户身份与客户端身份是否可区分?
  • 同意、Scope、角色、权益和对象授权是否分开管理?
  • 是否只传播最小化已验证上下文?
  • 是否移除调用方身份请求头并阻止后端直连绕过?
  • 是否测试密钥轮换、同意撤回、客户端禁用和 CIAM 故障?
  • 安全日志是否有用且不包含 Token 或过量客户数据?

常见问题

网关能否替代 CIAM?

不能。它可以执行 Token 与路由策略,但注册、认证旅程、账户恢复、身份联合、同意交互、客户生命周期和 Token 签发属于 CIAM 系统。

应用是否需要再次验证 Token?

这取决于信任边界。如果应用只能通过经过认证的网关链路访问,并接收最小化标准上下文,可能不必重复验证;如果 Token 会继续传递,或存在绕过路径,应用可能需要自行验证。应记录一套可辩护的模型。

Scope 能否替代对象级授权?

不能。Scope 描述面向某个受众的粗粒度权限,应用仍需执行账户、租户、所有权、工作流和数据状态规则。

后续步骤

使用 API 网关零信任架构构建身份边界,加入细粒度网关授权,并通过 API 访问日志审计保留证据。

获取方案