跳到主要内容

JWT Claim 映射

JWT 认证通过 jwt_subject 字段把一个外部身份绑定到一把调用方 API Key。这适合固定的 Agent 集群,但不适合企业身份提供方为成百上千的人员声明部门、群组或应用的场景:为每个人注册一把 Key 等于重复维护 IdP 的目录,而且 IdP 侧的变更永远不会自动传播。

Claim 映射弥补了这一缺口。映射是一条规则:它匹配 JWT 中已验证的声明(如部门名称、群组成员身份),并把请求解析到一把既有的调用方 API Key。被该规则放行的所有身份都以这把 Key 的模型与工具访问权限、限流和预算运行,而每个请求的用量仍然会归因到 Token 中的具体身份。

Claims 只用于选择运营者已创建的 Key。任何声明值都不会变成配置:Token 无法指定上游、无法扩大模型白名单、也无法创造预算。未命中任何映射的 Token 会被拒绝。

映射如何求值

对于每个携带 JWT Bearer 的请求,在完成 JWT 认证所述的完整信任提供方验证之后:

  1. 如果某把调用方 API Key 直接绑定了 Token 的 Subject(jwt_subject + jwt_provider),请求以该 Key 的身份运行。直接绑定对其 Subject 具有最高权威——包括已禁用的 Key,其请求仍会被拒绝。映射永远不会覆盖直接绑定。
  2. 否则,jwt_provider 指向命中信任提供方的已启用映射会按 priority 顺序求值——数值小者在前,同值按 name 排序——第一条 match 条件全部成立的映射决定所用的 Key。
  3. 如果没有任何映射命中,请求会被拒绝并返回 jwt_identity_unmapped。不存在默认放行。

映射的 match 是一组条件,所有条件必须同时成立:

字段说明
claim要检查的声明。点号用于遍历嵌套对象(例如 realm_access.roles)。缺失的声明永不匹配。
opexact——声明必须是等于 values 之一的字符串。contains——声明必须是数组,且其字符串元素中包含 values 之一(非字符串元素会被忽略)。类型与操作符不符的声明永不匹配。
values接受的值;任一匹配即条件成立。

映射指向的 Key 必须存在于同一环境中:Key 已被删除的映射会拒绝命中的 Token,而不是放行;且 jwt_provider 指向的信任提供方必须先完成 Token 验证,规则才会被考虑。

前提条件

以下工作流基于 JWT 认证的配置:一个已注册的 OIDC 信任提供方、一个模型,以及 curljq。你还需要一把调用方 API Key 作为共享的策略 Key——被放行身份继承的模型访问权限、限流和预算的载体。

导出基础 URL、管理员 Token、环境 ID 和策略 Key 的 ID:

export AISIX_CP="http://localhost:8080/api"
export AISIX_TOKEN="YOUR_ADMIN_TOKEN"
export ENV_ID="YOUR_ENVIRONMENT_ID"
export POLICY_KEY_ID="YOUR_API_KEY_ID"

创建 Claim 映射

department 声明等于 finance 的所有身份放行到策略 Key 下:

curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/claim_mappings" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "finance-dept",
"jwt_provider": "corp-keycloak",
"priority": 100,
"match": [
{"claim": "department", "op": "exact", "values": ["finance"]}
],
"resolve": {"api_key_id": "'"$POLICY_KEY_ID"'"}
}' | jq

响应会回显创建的映射。当环境中部分网关运行的版本早于 Claim 映射特性时,响应还会携带 warnings 数组——这些网关会完全跳过该规则,持续拒绝本应被放行的身份,请先升级它们再依赖此规则。

映射字段

字段说明
name映射名称,在环境内唯一,创建后不可更改。也是同优先级时的求值决胜依据。
jwt_provider此映射作用的 OIDC 提供方名称。映射永远不会匹配其他提供方验证的 Token,因此两个提供方无法选择彼此的 Key。
priority在该提供方的映射中的求值顺序:数值小者先求值。默认为 0
match声明条件,全部成立才算命中(见上表)。
resolve.api_key_id命中请求所运行的调用方 API Key 的 ID。
enabled映射是否参与求值。默认为 true;禁用的映射会保留但被跳过。

使用 PATCH .../claim_mappings/{claim_mapping_id} 更新映射(名称固定——重命名需删除后重建),使用 DELETE 删除。变更对新请求即时生效,无需重启网关;仅由已删除或已禁用映射放行的身份,会在网关同步变更后立即无法认证。

在 Dashboard 中,环境的 Claim 映射 页面管理同样的规则,并按求值顺序展示列表。

用优先级分层组合映射

规则通过优先级组合。给更精确的规则更小的数值,让它对其描述的身份优先生效,更宽泛的规则兜底其余。下面的示例解析到第二把策略 Key——先导出它的 ID:

export ADMIN_KEY_ID="YOUR_ADMIN_API_KEY_ID"

curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/claim_mappings" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "platform-admins",
"jwt_provider": "corp-keycloak",
"priority": 50,
"match": [
{"claim": "realm_access.groups", "op": "contains", "values": ["platform-admin"]}
],
"resolve": {"api_key_id": "'"$ADMIN_KEY_ID"'"}
}' | jq

同时携带 department: financeplatform-admin 群组的 Token 现在会命中 platform-admins(优先级 50)而不是 finance-dept(优先级 100)。需要不同策略的团队使用各自的策略 Key——每个团队一条规则即可。

按身份的用量归因

每个经映射放行的请求都会在其用量事件上记录三个归因字段,在环境的 Logs 中可见、可过滤,并包含在 CSV 导出中:

字段说明
jwt_subjectToken 身份声明的值——即使多个身份共享策略 Key,也能看出请求由谁发起。
jwt_provider验证该 Token 的信任提供方。
jwt_claim_mapping放行该请求的映射。通过 jwt_subject 直接绑定的身份此字段为空。

Logs 页面支持按 JWT Subject 过滤,用量事件 API 上的 jwt_subject / jwt_claim_mapping 查询参数以同样的方式收窄程序化读取。已配置的可观测性导出器会收到相同的字段——注意当你的身份声明携带姓名或邮箱时,Subject 属于个人数据,在与 Claim 映射同时启用前请先确认导出器的送达位置。

开源 AISIX 网关配置

在声明式 resources.yaml 中,使用 resolve.api_key 简写按 display_name 引用策略 Key:

resources.yaml
oidc_providers:
- name: corp-keycloak
issuer: https://sso.example.com/realms/agents
audiences: ["aisix-gateway"]

api_keys:
- display_name: finance-policy-key
key_env: FINANCE_POLICY_KEY
allowed_models: ["gpt-4o-prod"]

claim_mappings:
- name: finance-dept
jwt_provider: corp-keycloak
priority: 100
match:
- claim: department
op: exact
values: ["finance"]
resolve:
api_key: finance-policy-key

文件加载器会在加载时解析引用,并拒绝未知的提供方或 Key 名称、空的条件列表,以及 jwt_provider 指向文件中不存在的提供方的映射——拼写错误会导致加载失败,而不是静默地永不匹配。

拒绝行为

映射认证扩展了 JWT 拒绝原因表。验证通过但未命中任何映射的 Token——或命中的映射所指向的 Key 已不存在——会在请求到达任何模型提供方、MCP 服务器或向量存储之前被拒绝,返回 jwt_identity_unmapped(HTTP 401)。网关的 aisix_auth_decisions_total 指标为运维提供更细的原因区分(jwt_identity_unmappedclaim_mapping_target_missingjwt_binding_ambiguous)。

后续步骤