keycloak~通过用户ID直接置换token
Keycloak Token Exchange + Impersonation(运维配置)
门户 GET /kc/exchange-token 新流程:
- 用请求头里的 WSO2 应用 access_token 调 mcp-admin:
POST /internal/wso2/token/authz-user→ 得到kcUserId(来自idn_oauth2_access_token.AUTHZ_USER) - 用 Keycloak 客户端
wso2做 client_credentials 拿到服务账号 token - 再用 Token Exchange,带
requested_subject={kcUserId},签发该用户的 KC access_token
因此 不要求 WSO2 应用 token 带 openid scope,但要求 Keycloak 客户端具备 Service Account + Token Exchange(用户模拟) 权限。
以下以客户端 ID wso2、Realm fabao 为例(与 application-*.yml 中 keycloak.client-id 一致)。
一、客户端基础开关
登录 Keycloak Admin Console → Realm fabao → Clients → wso2:
| 项 | 建议值 |
|---|---|
| Client authentication | On |
| Authentication flow / Standard flow | 按现有对接保留 |
| Service accounts roles | On(必须,用于 client_credentials) |
| Direct access grants | 可不依赖 |
| Valid redirect URIs / Web origins | 按现有配置 |
保存后,在 Credentials 页确认 client-secret 与各环境 KEYCLOAK_WSO2_CLIENT_SECRET / yml 一致。
二、开通 Token Exchange(推荐:Permissions 模式,适用于多数现网 KC)
不同 Keycloak 大版本菜单位置略有差异,目标是:允许客户端 wso2 用自己的服务账号 token,对任意(或指定)用户做 requested_subject 交换。
2.1 开启客户端 Permissions
- Clients →
wso2→ Permissions(或 Authorization) - 将 Permissions enabled 设为 On
- 等待出现权限列表(含
token-exchange、可能还有impersonate等)
2.2 配置 token-exchange 权限
- 打开权限 token-exchange
- Decision Strategy:Affirmative(或按现网规范)
- 关联一条 Client Policy:
- 新建 Policy → 类型 Client
- Clients:勾选
wso2 - Logic:Positive
- 将该 Policy 绑到
token-exchangePermission 并 Save

2.3 配置用户 Impersonation(requested_subject 必需)
requested_subject 属于「用 A 的 token 换 B 用户的 token」,除 token-exchange 外通常还要 impersonation:
方式 A(常用,粒度到用户权限):
- Realm → Users → 任意用户 → Permissions(需先在 Realm Settings 开启 Users permissions;部分版本在 Realm settings → User registration / Permissions)
- 或:Realm → Permissions / Authorization 中找到用户 impersonation 相关权限
- 更稳妥的现网做法见 方式 B
方式 B(服务账号角色,配置简单,权限较大):
- Clients →
wso2→ Service account roles - Client roles 来源选 realm-management
- 分配角色:
impersonation(必须)- 若列表里还有与 token-exchange 相关的角色可一并勾选(视版本而定)
- Save
方式 B 权限面更大(可模拟 realm 内用户),仅限受信的内部客户端
wso2,密钥严格保管。

2.4(可选)Audience / Client scope
若换出的用户 token 需要特定 audience 或 scope(门户后续 jwt-bearer 进 APIM 依赖 claim):
- Clients →
wso2→ Client scopes:保证默认 scope 含业务所需(至少openid,与 ymlkeycloak.scope一致) - Token Exchange 请求里门户已传
scope=openid(可配置)
三、自检(运维验证)
把 {KC_TOKEN_URI}、{CLIENT_ID}、{CLIENT_SECRET}、{KC_USER_ID} 换成实际值。
3.1 client_credentials
curl -s -X POST '{KC_TOKEN_URI}' \
-u '{CLIENT_ID}:{CLIENT_SECRET}' \
-d 'grant_type=client_credentials' \
-d 'scope=openid'
应返回 access_token。记下为 ACTOR_TOKEN。
3.2 token-exchange + requested_subject
curl -s -X POST '{KC_TOKEN_URI}' \
-u '{CLIENT_ID}:{CLIENT_SECRET}' \
-d 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
-d 'subject_token={ACTOR_TOKEN}' \
-d 'subject_token_type=urn:ietf:params:oauth:token-type:access_token' \
-d 'requested_token_type=urn:ietf:params:oauth:token-type:access_token' \
-d 'requested_subject={KC_USER_ID}' \
-d 'scope=openid'
成功:返回用户 access_token;解码 JWT 后 sub(或等价用户标识)应为该用户。
常见失败:
| 现象 | 处理 |
|---|---|
Client not allowed to exchange / not allowed to impersonate |
补全 2.2 / 2.3 |
Client not enabled for service accounts |
打开 Service accounts roles |
Invalid user credentials / user not found |
确认 AUTHZ_USER / kcUserId 是 Realm 内真实用户 ID(UUID)或用户名,与 Keycloak 用户匹配 |
unauthorized_client |
client-secret 或 client-id 错误 |
浙公网安备 33010602011771号