12.2. 传统令牌交换


注意

令牌交换是 预览 的,不受支持。此功能默认为禁用。

使用-- features=preview or-- features=token-exchange启用服务器

令牌交换 是技术预览,不受支持。

注意

要使用 内部 令牌交换流,请启用 admin-fine-grained-authz 功能。详情请参阅 启用和禁用功能 章节。

12.2.1. 令牌交换的工作方式

在 Red Hat build of Keycloak 中,令牌交换是使用一组凭证或令牌获取完全不同的令牌的过程。客户端可能想要在不太信任的应用上调用,因此可能希望降级它具有的当前令牌。客户端可能希望为链接的社交供应商帐户存储的令牌交换红帽构建的 Keycloak 令牌。您可能希望信任外部令牌,由其他红帽构建 Keycloak 域或外部 IDP 最小化。客户端可能需要模拟用户。以下是红帽围绕令牌交换构建的 Keycloak 当前功能的简短概述。

  • 客户端可以交换为特定客户端创建用于不同客户端的新令牌的现有红帽构建的 Keycloak 令牌
  • 客户端可以为外部令牌交换现有红帽构建的 Keycloak 令牌,例如链接的 Facebook 帐户
  • 客户端可以为红帽构建的 Keycloak 令牌交换外部令牌。
  • 客户端可以模拟用户

红帽构建的 Keycloak 中的令牌交换是 IETF 中对 OAuth Token Exchange 规格的非常松散的实现。我们已对其进行扩展,忽略其中的一些内容,并松散地解释规格的其他部分。它是对域的 OpenID Connect 令牌端点的简单授权类型调用。

/realms/{realm-name}/protocol/openid-connect/token
Copy to Clipboard Toggle word wrap

它接受表单参数(application/x-www-form-urlencoded)作为输入,输出取决于您请求交换的令牌类型。令牌交换是一个客户端端点,因此请求必须为调用客户端提供身份验证信息。公共客户端将其客户端标识符指定为表单参数。机密客户端也可以使用表单参数传递其客户端 ID 和机密(Basic Auth),或者您的 admin 已在您的域中配置了客户端身份验证流。

12.2.1.1. 表单参数

client_id
必需的可能。使用表单参数进行身份验证的客户端需要此参数。如果您使用 Basic Auth、客户端 JWT 令牌或客户端证书身份验证,则不指定此参数。
client_secret
必需可能是。使用表单参数进行身份验证并将客户端 secret 用作凭证的客户端需要此参数。如果您的域中客户端通过不同的方式进行身份验证,则不要指定此参数。
grant_type
必需。参数的值必须是 urn:ietf:params:oauth:grant-type:token-exchange
subject_token
可选。代表要发出请求的人,代表方身份的安全令牌。如果您要为新令牌交换现有令牌,则需要它。
subject_issuer
可选。标识 subject_token 的签发者。如果令牌来自当前域,则可以留空,或者是否可以从 subject_token_type 确定签发者。否则,需要指定它。有效值是为 您的域配置的身份提供程序 的别名。或者由特定 身份提供程序配置的签发者声明标识符
subject_token_type
可选。这个参数是使用 subject_token 参数传递的令牌类型。如果 subject_token 来自 realm,则默认为 urn:ietf:params:oauth:token-type:access_token,则默认为 urn:ietf:params:oauth:token-type:access_token。如果是一个外部令牌,则根据 subject_issuer 的要求,可能需要指定这个参数。
requested_token_type
可选。此参数代表了客户端要交换的令牌类型。目前只支持 oauth 和 OpenID Connect 令牌类型。这的默认值取决于它是否为 urn:ietf:params:oauth:token-type:refresh_token,在这种情况下,您将会在响应中返回访问令牌和刷新令牌。其他适当的值有 urn:ietf:params:oauth:token-type:access_tokenurn:ietf:params:oauth:token-type:id_token
受众
可选。这个参数指定您希望新令牌 mint 的目标客户端。
requested_issuer
可选。这个参数指定客户端需要外部供应商 mint 的令牌。它必须是域中 配置的身份提供程序 的别名。
requested_subject
可选。如果您的客户端希望模拟其他用户,则指定用户名或用户 id。
scope
可选。此参数代表客户端要请求的 OAuth 和 OpenID Connect 范围的目标集合。返回的范围是 scope 参数和访问令牌范围的 Cartesian 产品。
注意

我们目前仅支持 OpenID Connect 和 OAuth 交换。以后可能会添加对基于 SAML 的客户端和身份提供程序的支持,具体取决于用户的需求。

12.2.1.2. 来自令牌交换请求的响应

来自交换调用的成功响应将返回 HTTP 200 响应代码,其内容类型取决于 requested-token-typerequested_issuer。OAuth 请求的令牌类型将返回 JSON 文档,如 OAuth 令牌交换 规格中所述。

{
   "access_token" : ".....",
   "refresh_token" : ".....",
   "expires_in" : "...."
 }
Copy to Clipboard Toggle word wrap

请求刷新令牌的客户端将在响应中返回访问和刷新令牌。仅请求访问令牌类型的客户端仅在响应中获取访问令牌。对于通过 requested_issuer 参数请求外部签发者的客户端,可能无法包含过期信息。

错误响应通常属于 400 HTTP 响应代码类别,但可能会返回其他错误状态代码,具体取决于错误的严重性。错误响应可能会包含的内容,具体取决于 requested_issuer。基于 OAuth 的交换可能会返回 JSON 文档,如下所示:

{
   "error" : "...."
   "error_description" : "...."
}
Copy to Clipboard Toggle word wrap

根据交换类型,可能会返回其他错误声明。例如,如果用户没有到身份提供程序的链接,OAuth 身份提供程序可能包含额外的 account-link-url 声明。此链接可用于客户端发起的链接请求。

注意

令牌交换设置需要了解 admin 权限(如需更多信息,请参阅 服务器管理指南 )。您需要授予客户端权限来交换。本章稍后将对此进行进一步的讨论。

本章的其余部分讨论了设置要求,并为不同的交换场景提供示例。为了简单起见,让我们调用当前域以 内部 令牌为 mint 的令牌,以及外部域或身份提供程序以 外部 令牌为 mint 的令牌。

12.2.2. 内部令牌交换

注意

对于内部令牌交换的内部令牌,建议使用 标准令牌交换,而不是使用以下所述的传统令牌交换流。官方支持标准令牌交换。

使用内部令牌进行令牌交换,您已将令牌 mint 到一个特定的客户端,并且您想要为不同的目标客户端交换此令牌。您为什么要执行此操作?当客户端具有对其自身的令牌 mint 时,通常会发生这种情况,并且需要对访问令牌中需要不同声明和权限的其他应用程序发出额外的请求。如果您需要执行"权限降级",您可能需要进行"权限降级",因为应用程序需要在不太信任的应用程序上调用,而您不想传播当前的访问令牌。

12.2.2.1. 为交换授予权限

希望为不同客户端交换令牌的客户端需要在 Admin 控制台中获得授权。您需要在目标客户端中定义令牌交换权限。

图 12.3. 目标客户端权限

流程

  1. 将" 启用" 的权限切换为 On

    图 12.4. 目标客户端权限

    该页面显示 token-exchange 链接。

  2. 单击该链接,以开始定义权限。

    这时将显示此设置页面。

    图 12.5. 目标客户端交换权限设置

  3. 在屏幕顶部的面包屑导航栏中点 Client details
  4. 为这个权限定义策略。
  5. 在屏幕顶部的面包屑导航栏中点 Authorization
  6. 为这个权限定义策略。
  7. Policies 选项卡。
  8. Create policy 按钮创建 客户端 策略。

    图 12.6. 客户端策略创建

  9. 在作为请求令牌交换的经过身份验证的用户中输入。
  10. 创建此策略后,返回到目标客户端的 token-exchange 权限,并添加您刚才定义的客户端策略。

    图 12.7. 应用客户端策略

您的客户端现在有调用的权限。如果不正确这样做,如果您尝试进行交换,您将会收到 403 Forbidden 响应。

12.2.2.2. 发出请求

当客户端针对另一个客户端为目标的令牌交换现有令牌时,您可以使用 audience 参数。此参数必须是您在管理控制台中配置的目标客户端的客户端标识符。

curl -X POST \
    -d "client_id=starting-client" \
    -d "client_secret=the client secret" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "subject_token=...." \
    --data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:refresh_token" \
    -d "audience=target-client" \
    http://localhost:8080/realms/myrealm/protocol/openid-connect/token
Copy to Clipboard Toggle word wrap

subject_token 参数必须是目标域的访问令牌。如果您的 requested_token_type 参数是刷新令牌类型,则响应将同时包含访问令牌、刷新令牌和过期。以下是您从此调用返回的示例 JSON 响应:

如果没有设置 audience 参数,则参数的值默认为提交令牌交换请求的客户端。

与机密客户端不同,不允许公共客户端使用来自其他客户端的令牌执行令牌交换。如果您要传递 subject_token,签发令牌的(机密)客户端应与发出请求的客户端匹配,或者如果向其他客户端发出,则使请求的客户端应设置为令牌的使用者。

如果您明确设置目标 audience (客户端与发出请求的客户端不同),您应确保为客户端集配置了 token-exchange 范围权限,设置 audience 权限以允许客户端成功完成交换。

{
   "access_token" : "....",
   "refresh_token" : "....",
   "expires_in" : 3600
}
Copy to Clipboard Toggle word wrap

12.2.3. 外部令牌交换的内部令牌

您可以为外部身份提供程序以 mint 的外部令牌交换域令牌。此外部身份提供程序必须在管理控制台的 Identity Provider 部分中配置。目前只支持基于 OAuth/OpenID Connect 的外部身份提供程序,包括所有社交提供程序。红帽构建的 Keycloak 不对外部供应商执行后端通道交换。因此,如果帐户没有链接,您将无法获取外部令牌。要能够获取外部令牌,必须满足这些条件之一:

  • 用户必须至少使用外部身份提供程序登录
  • 用户必须通过用户帐户服务与外部身份提供程序相关联
  • 用户帐户通过外部身份提供程序使用 Client Initiated Account Linking API 链接。

最后,外部身份提供程序必须配置为存储令牌,或者上述一个操作必须使用与您交换的内部令牌相同的用户会话执行。

如果帐户未链接,交换响应将包含您可以用来建立它的链接。在 Making the Request 部分中将对此进行详细讨论。

12.2.3.1. 为交换授予权限

外部令牌交换请求的内部将拒绝 403, Forbidden 响应,直到您向调用客户端授予了与外部身份提供程序交换令牌的权限。要向客户端授予权限,请转至身份提供程序的配置页面,进入 Permissions 选项卡。

图 12.8. 身份提供程序权限

流程

  1. 将" 启用" 的权限切换为 On

    图 12.9. 身份提供程序权限

    页面中显示 token-exchange 链接。

  2. 单击链接以开始定义权限。

    此时会出现此设置页面。

    图 12.10. 身份提供程序交换权限设置

  3. 在屏幕顶部的面包屑导航栏中点 Client details
  4. Policies 选项卡创建客户端策略。

    图 12.11. 客户端策略创建

  5. 输入作为请求令牌交换的经过身份验证的用户的起始客户端。
  6. 返回到身份提供程序的 token-exchange 权限,并添加您刚才定义的客户端策略。

    图 12.12. 应用客户端策略

您的客户端现在有调用的权限。如果不正确这样做,如果您尝试进行交换,您将会收到 403 Forbidden 响应。

12.2.3.2. 发出请求

当客户端将现有内部令牌交换到外部令牌时,您要提供 requested_issuer 参数。参数必须是配置的身份提供程序的别名。

curl -X POST \
    -d "client_id=starting-client" \
    -d "client_secret=the client secret" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "subject_token=...." \
    --data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
    -d "requested_issuer=google" \
    http://localhost:8080/realms/myrealm/protocol/openid-connect/token
Copy to Clipboard Toggle word wrap

subject_token 参数必须是目标域的访问令牌。requested_token_type 参数必须是 urn:ietf:params:oauth:token-type:access_token 或 left blank。目前不支持其他请求的令牌类型。以下是您从此调用中获取的成功 JSON 响应示例:

{
   "access_token" : "....",
   "expires_in" : 3600
   "account-link-url" : "https://...."
}
Copy to Clipboard Toggle word wrap

如果外部身份提供程序没有出于某种原因链接,您将获得带有此 JSON 文档的 HTTP 400 响应代码:

{
   "error" : "....",
   "error_description" : "..."
   "account-link-url" : "https://...."
}
Copy to Clipboard Toggle word wrap

错误 声明将是 token_expirednot_linked。提供了 account-link-url 声明,以便客户端能够执行客户端 初始帐户链接。大多数(如果不是全部)供应商需要通过浏览器 OAuth 协议链接。使用 account-link-url 仅向其添加一个 redirect_uri 查询参数,您可以转发浏览器来执行链接。

12.2.4. 外部令牌到内部令牌交换

您可以为内部令牌信任和交换外部令牌,供外部身份提供程序使用。这可用于在域间桥接,或者只是信任来自您的社交提供程序的令牌。它的工作方式与身份提供程序浏览器登录相似,如果新用户不存在,则会将新用户导入到您的域中。

注意

外部令牌交换的当前限制是,如果外部令牌映射到现有用户,则不允许交换,除非现有用户已具有到外部身份提供程序的帐户链接。

当交换完成后,将在域中创建用户会话,并且您将收到访问和刷新令牌,具体取决于 requested_token_type 参数值。您应该注意,这个新用户会话将保持活动状态,直到超时或直至您调用通过这个新访问令牌的域的 logout 端点。

这些类型的更改需要在管理门户中配置的身份提供程序。

注意

目前不支持 SAML 身份提供程序。terwitter 令牌无法交换。

12.2.4.1. 为交换授予权限

在进行外部令牌交换之前,您可以为调用客户端授予该交换的权限。授予此权限的方式与 授予外部权限的内部 相同。

如果您还提供了一个 audience 参数,其值指向调用者之外的其他客户端,还必须授予调用客户端权限以与 audience 参数中特定的目标客户端交换。本节的前面 讨论了如何 执行此操作。

12.2.4.2. 发出请求

subject_token_type 必须是 urn:ietf:params:oauth:token-type:access_tokenurn:ietf:params:oauth:token-type:jwt。如果类型是 urn:ietf:params:oauth:token-type:access_token,您指定 subject_issuer 参数,它需要是配置的身份供应商的别名。如果类型是 urn:ietf:params:oauth:token-type:jwt,则提供程序将通过 JWT 中的 iss (issuer)声明匹配,该声明必须是提供程序的别名,或者在提供程序配置中注册签发者。

若要验证,如果令牌是访问令牌,则将调用提供程序的用户 info 服务来验证令牌。成功调用将意味着访问令牌有效。如果主题令牌是 JWT,并且如果提供程序启用了签名验证,则会尝试该验证,否则它将默认调用用户信息服务来验证令牌。

默认情况下,内部令牌 mint 将使用调用客户端来确定令牌中的什么部分使用为调用客户端定义的协议映射器。或者,您可以使用 audience 参数指定不同的目标客户端。

curl -X POST \
    -d "client_id=starting-client" \
    -d "client_secret=the client secret" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "subject_token=...." \
    -d "subject_issuer=myOidcProvider" \
    --data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
    -d "audience=target-client" \
    http://localhost:8080/realms/myrealm/protocol/openid-connect/token
Copy to Clipboard Toggle word wrap

如果您的 requested_token_type 参数是刷新令牌类型,则响应将同时包含访问令牌、刷新令牌和过期。以下是您从此调用返回的示例 JSON 响应:

{
   "access_token" : "....",
   "refresh_token" : "....",
   "expires_in" : 3600
}
Copy to Clipboard Toggle word wrap

12.2.5. 模拟(Impersonation)

对于内部和外部令牌交换,客户端可以代表用户请求模拟其他用户。例如,您可能有一个需要模拟用户的 admin 应用程序,以便支持工程师能够调试问题。

注意

此处提到的模拟方案与令牌交换 规格的模拟概念 不同。该规范不支持模拟受不同主题的令牌。规格语义意味着"获取客户端"而不是"调整用户"。

12.2.5.1. 为交换授予权限

主题令牌所代表的用户必须具有模拟其他用户的权限。有关如何启用 此权限,请参阅服务器管理指南。它可以通过角色或精细的 admin 权限来完成。

12.2.5.2. 发出请求

根据其他章节中所述发出请求,但额外指定 requested_subject 参数。此参数的值必须是用户名或用户 ID。

curl -X POST \
    -d "client_id=starting-client" \
    -d "client_secret=the client secret" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "subject_token=...." \
    --data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
    -d "audience=target-client" \
    -d "requested_subject=wburke" \
    http://localhost:8080/realms/myrealm/protocol/openid-connect/token
Copy to Clipboard Toggle word wrap

12.2.6. Direct Naked Impersonation

您可以在不提供 subject_token 的情况下发出内部令牌交换请求。这称为直接的模拟,因为它在客户端中放置了大量信任,因为客户端可以模拟域中的任何用户。对于无法获取主题令牌来交换的应用程序,您可能需要进行桥接。例如,您可以集成一个通过 LDAP 直接登录的传统应用程序。在这种情况下,传统应用程序能够对用户本身进行身份验证,但不能获取令牌。

警告

为客户端启用直接模拟非常风险。如果客户端的凭据被盗了,则该客户端可以模拟系统中的任何用户。

12.2.6.1. 为交换授予权限

如果提供了 audience 参数,则调用客户端必须具有与客户端交换的权限。本章前文中会讨论如何设置此设置。

另外,必须授予调用客户端的权限来模拟用户。

流程

  1. 点菜单中的 Users
  2. 单击权限选项卡。

    图 12.13. 用户权限

  3. 将" 启用" 的权限切换为 On

    图 12.14. 身份提供程序权限

    该页面显示一个 模拟 链接。

  4. 单击该链接,以开始定义权限。

    这时将显示此设置页面。

    图 12.15. 用户获取权限设置

  5. 在屏幕顶部的面包屑导航栏中点 Client details
  6. 为这个权限定义策略。
  7. 进入 Policies 选项卡,再创建一个客户端策略。

    图 12.16. 客户端策略创建

  8. 输入作为请求令牌交换的经过身份验证的用户的起始客户端。
  9. 返回到用户的 模拟 权限,并添加您刚才定义的客户端策略。

    图 12.17. 应用客户端策略

您的客户端现在有模拟用户的权限。如果不正确,如果您尝试进行此类交换,您将会收到 403 Forbidden 响应。

注意

不允许公共客户端进行直接模拟。

12.2.6.2. 发出请求

要发出请求,只需指定 requested_subject 参数。这必须是有效用户的用户名或用户 ID。如果想要,您还可以指定 audience 参数。

curl -X POST \
    -d "client_id=starting-client" \
    -d "client_secret=the client secret" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "requested_subject=wburke" \
    http://localhost:8080/realms/myrealm/protocol/openid-connect/token
Copy to Clipboard Toggle word wrap

12.2.7. 使用服务帐户扩展权限模型

在授予客户端交换权限时,您不必为每个和每个客户端手动启用这些权限。如果客户端关联有服务帐户,您可以使用角色将权限分组在一起,并通过将角色分配给客户端的服务帐户来分配交换权限。例如,您可以定义一个 naked-exchange 角色,以及具有该角色的任何服务帐户都可以执行 naked 交换。

12.2.8. 交换漏洞

当您开始允许令牌交换时,您必须了解和小心。

第一个是公共客户端。公共客户端没有或需要客户端凭证才能执行交换。具有有效令牌的任何人都能够 模拟 公共客户端并执行允许公共客户端执行的交换。如果有由您的域管理的不可靠客户端,公共客户端可能会在您的权限模型中打开漏洞。正因如此,直接利用的交换不允许公共客户端,如果调用客户端是公共的客户端,将中止并出错。

对于域令牌,可以交换bookbook、Google 等社交令牌。请小心和 vigilante,了解交换令牌允许执行的操作,因为这些社交 Web 站点上不会创建假帐户。使用默认角色、组和身份提供程序映射程序来控制将哪些属性和角色分配给外部社交用户。

直接利用的交换非常危险。在调用客户端中造成大量信任,它将永远不会泄漏其客户端凭证。如果这些凭证被泄漏,则 thief 可以模拟系统中的任何人。这与已具有现有令牌的机密客户端的直接对比。您有两个验证因素:访问令牌和客户端凭证,并且您只处理一个用户。因此,请谨慎使用直接的交换。

返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat