keycloak~通过用户ID直接置换token

Keycloak Token Exchange + Impersonation(运维配置)

门户 GET /kc/exchange-token 新流程:

  1. 用请求头里的 WSO2 应用 access_token 调 mcp-admin:POST /internal/wso2/token/authz-user → 得到 kcUserId(来自 idn_oauth2_access_token.AUTHZ_USER)
  2. 用 Keycloak 客户端 wso2 做 client_credentials 拿到服务账号 token
  3. 再用 Token Exchange,带 requested_subject={kcUserId},签发该用户的 KC access_token

因此 不要求 WSO2 应用 token 带 openid scope,但要求 Keycloak 客户端具备 Service Account + Token Exchange(用户模拟) 权限。

以下以客户端 ID wso2、Realm you_realm 为例(与 application-*.yml 中 keycloak.client-id 一致)。


一、客户端基础开关

登录 Keycloak Admin Console → Realm you_realm → 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

  1. Clients → wso2 → Permissions(或 Authorization)
  2. 将 Permissions enabled 设为 On
  3. 等待出现权限列表(含 token-exchange、可能还有 impersonate 等)

2.2 配置 token-exchange 权限

  1. 打开权限 token-exchange
  2. Decision Strategy:Affirmative(或按现网规范)
  3. 关联一条 Client Policy:
    • 新建 Policy → 类型 Client
    • Clients:勾选 wso2
    • Logic:Positive
  4. 将该 Policy 绑到 token-exchange Permission 并 Save

2.3 配置用户 Impersonation(requested_subject 必需)

requested_subject 属于「用 A 的 token 换 B 用户的 token」,除 token-exchange 外通常还要 impersonation:

方式 A(常用,粒度到用户权限):

  1. Realm → Users → 任意用户 → Permissions(需先在 Realm Settings 开启 Users permissions;部分版本在 Realm settings → User registration / Permissions)
  2. 或:Realm → Permissions / Authorization 中找到用户 impersonation 相关权限
  3. 更稳妥的现网做法见 方式 B

方式 B(服务账号角色,配置简单,权限较大):

  1. Clients → wso2 → Service account roles
  2. Client roles 来源选 realm-management
  3. 分配角色:
    • impersonation(必须)
    • 若列表里还有与 token-exchange 相关的角色可一并勾选(视版本而定)
  4. Save

方式 B 权限面更大(可模拟 realm 内用户),仅限受信的内部客户端 wso2,密钥严格保管。

2.4(可选)Audience / Client scope

若换出的用户 token 需要特定 audience 或 scope(门户后续 jwt-bearer 进 APIM 依赖 claim):

  • Clients → wso2 → Client scopes:保证默认 scope 含业务所需(至少 openid,与 yml keycloak.scope 一致)
  • Token Exchange 请求里门户已传 scope=openid(可配置)

三、 认证的流程

sequenceDiagram participant Client participant YourSite as YourSite participant Wso2Db as WSO2AM_idn_oauth2 participant KC as Keycloak Client->>YourSite: Authorization WSO2 app token Portal->>Wso2Db: ACCESS_TOKEN 查 AUTHZ_USER Wso2Db-->>YourSite: kcUserId Portal->>KC: client_credentials KC-->>YourSite: serviceToken Portal->>KC: token-exchange requested_subject=kcUserId KC-->>YourSite: user access_token Portal-->>Client: Authorization Bearer kcToken

四、自检(运维验证)

把 {KC_TOKEN_URI}、{CLIENT_ID}、{CLIENT_SECRET}、{KC_USER_ID} 换成实际值。

4.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。

4.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 错误

4.3 联调门户链路

  1. 用真实 WSO2 应用 token:
curl -i -X GET 'https://{portal-gateway}/api-portal/kc/exchange-token' \
  -H 'Authorization: Bearer {WSO2_APP_ACCESS_TOKEN}'
  1. 响应头应有 Authorization: Bearer {KC_ACCESS_TOKEN}。

  2. 若失败,先查 mcp-admin:

curl -s -X POST 'http://your_site-mcp-admin...:8080/internal/wso2/token/authz-user' \
  -H 'Content-Type: application/json' \
  -d '{"accessToken":"{WSO2_APP_ACCESS_TOKEN}"}'

应返回 code=200 且 data.kcUserId 非空;再用该 id 跑 3.2。


五、与旧方案差异(给运维/研发对齐)

项 旧(subject_issuer) 新(requested_subject)
WSO2 token scope 常需可被 KC 校验的 openid/JWT 仅需库表能查到明文 ACCESS_TOKEN
KC 配置重点 Identity Provider / 外部令牌信任 Service Account + Token Exchange + Impersonation
yml subject-issuer 主流程使用 保留兼容,主流程不再传

六、安全注意

  • wso2 客户端密钥等同「可代表任意用户发 token」,禁止下发到前端或写入公开仓库。
  • /internal/wso2/token/authz-user 仅集群内访问;后续若恢复 HMAC,与积分内部接口同一套 internal-api 密钥。
  • 定期审计 Service account 的 impersonation 角色是否仍必要。
posted @ 2026-09-17 13:34  张占岭  阅读(39)  评论(0)    收藏  举报