保护应用程序和服务指南


Red Hat build of Keycloak 26.4

Red Hat Customer Content Services

摘要

本指南包含使用红帽构建的 Keycloak 26.4 保护应用程序和服务的信息。

第 1 章 计划保护应用程序和服务

了解保护应用程序的基本概念。

作为 OAuth2、OpenID Connect 和 SAML 兼容服务器,只要它们使用的任何技术堆栈支持任何协议,红帽构建的 Keycloak 可以保护任何应用程序和服务。有关红帽构建的 Keycloak 支持的安全协议的更多详细信息,请参阅 服务器管理指南

对其中一些协议的大部分支持已经通过它们所使用的编程语言、框架或反向代理中获得。利用应用程序生态系统中已提供的支持是使应用程序完全符合安全标准和最佳实践的关键方面,以便您避免了供应商绑定。

对于某些编程语言,红帽构建的 Keycloak 提供库,用于尝试填补特定安全协议缺少支持或提供更加丰富的、紧密地与服务器集成之间的差距。这些库由 Keycloak Client Adapters 知道,如果您无法依赖应用程序生态系统中可用的内容,则应将它们用作最后的手段。

1.1. 保护应用程序和服务的基本步骤

以下是保护应用程序或红帽构建的 Keycloak 服务的基本步骤。

  1. 使用以下选项之一将客户端注册到域中:

    • Red Hat build of Keycloak Admin 控制台
    • 客户端注册服务
    • CLI
  2. 使用这些选项在应用程序中启用 OpenID Connect 或 SAML 协议:

    • 从应用程序生态系统中利用现有的 OpenID Connect 和 SAML 支持
    • 使用红帽构建的 Keycloak 适配器

本指南提供这些步骤的详细信息。您可以通过管理控制台,在服务器管理指南 中找到有关如何将客户端注册到红帽 Keycloak 的红帽构建信息。

1.2. 开始使用

Red Hat build of Keycloak Quickstarts Repository 提供了有关如何使用不同编程语言和框架保护应用程序和服务的示例。通过完成其文档和代码库,您将了解应用程序和服务所需的基本更改,以便通过红帽构建的 Keycloak 来保护它。

另外,请参阅以下小节以了解 OpenID Connect 和 SAML 协议的可信和知名客户端实现的建议。

1.2.1. OpenID Connect

1.2.1.1. JavaScript (客户端)
1.2.1.2. Node.js (服务器)

1.2.2. SAML

1.2.2.1. Java

1.3. 术语

本指南中使用了这些术语:

  • 客户端是 与红帽构建的 Keycloak 交互的实体,用来验证用户身份并获取令牌。大多数情况下,客户端都是代表代表用户提供单点登录体验的用户的应用程序和服务,并使用服务器发布的令牌访问其他服务。客户端也可以是只对获取令牌并由其代表访问其他服务的实体。
  • 应用程序 包括广泛的应用程序,适用于每个协议的特定平台
  • 客户端适配器 是与红帽构建的 Keycloak 轻松保护应用程序和服务的库。它们提供与底层平台和框架的紧密集成。
  • 创建客户端注册客户端是相同的操作。创建 客户端是使用管理控制台创建客户端的术语。注册客户端 是使用红帽构建的 Keycloak 客户端注册服务注册客户端的术语。
  • 服务帐户是一种 客户端,能够代表其自行获取令牌。

使用 OpenID Connect 与红帽构建的 Keycloak 来保护应用程序和服务。

2.1. 可用端点

作为完全兼容的 OpenID Connect Provider 实现,红帽构建的 Keycloak 会公开一组端点,供应用程序和服务用来验证和授权其用户。

本节论述了应用程序和服务在与红帽构建的 Keycloak 交互时应使用的一些关键端点。

2.1.1. Endpoints

要理解的最重要端点是 已知的 配置端点。它列出了与红帽构建的 Keycloak 中 OpenID Connect 实现相关的端点和其他配置选项。端点是:

/realms/{realm-name}/.well-known/openid-configuration

要获取完整的 URL,请添加红帽构建的 Keycloak 的基本 URL,并将 {realm-name} 替换为您的域的名称。例如:

http://localhost:8080/realms/{realm-name}/.well-known/openid-configuration

有些 RP 库从此端点检索所有必要的端点,但对于其他库,您可能需要单独列出端点。

2.1.1.1. 授权端点
/realms/{realm-name}/protocol/openid-connect/auth

授权端点执行最终用户的身份验证。此身份验证通过将用户代理重定向到此端点来实现。

如需了解更多详细信息,请参阅 OpenID Connect 规格中的 Authorization Endpoint 部分。

2.1.1.2. 令牌端点
/realms/{realm-name}/protocol/openid-connect/token

令牌端点用于获取令牌。令牌可以通过交换授权代码或直接提供凭据(根据所使用的流)来获取。令牌端点也用于在过期时获取新的访问令牌。

如需了解更多详细信息,请参阅 OpenID Connect 规格中的 Token Endpoint 部分。

2.1.1.3. userinfo 端点
/realms/{realm-name}/protocol/openid-connect/userinfo

userinfo 端点返回有关经过身份验证的用户的标准声明;此端点受 bearer 令牌保护。

如需了解更多详细信息,请参阅 OpenID Connect 规格中的 Userinfo 端点 部分。

2.1.1.4. 退出端点
/realms/{realm-name}/protocol/openid-connect/logout

logout 端点会注销经过身份验证的用户。

用户可以重定向到端点,这会导致活跃用户会话被注销。然后,用户代理会被重定向到应用程序。这在 服务器管理指南 的 RP-Initiated logout 部分中有 更多详细信息。

警告

端点也可以由应用直接调用。要直接调用此端点,需要包含刷新令牌以及验证客户端所需的凭据。但是,因为旧的红帽构建 Keycloak OIDC Java 适配器或 Elytron Wildfly OIDC 适配器,这才支持非标准的注销消息格式。不建议直接从应用程序使用它。对于注销用户,建议使用 OIDC/SAML 协议标准注销 或红帽构建 Keycloak Admin 控制台 (或其他管理 REST API) 或红帽构建 Keycloak 帐户控制台 (或帐户 REST API 的其它方式)。

2.1.1.5. 证书端点
/realms/{realm-name}/protocol/openid-connect/certs

证书端点返回域启用的公钥,编码为 JSON Web 密钥(JWK)。根据域设置,可以启用一个或多个密钥来验证令牌。如需更多信息,请参阅 服务器管理指南和 JSON Web 密钥规格

如需了解更多详细信息,请参阅 OpenID Connect Discovery 规格。

2.1.1.6. 内省端点
/realms/{realm-name}/protocol/openid-connect/token/introspect

内省端点用于检索令牌的活动状态。换句话说,您可以使用它来验证访问或刷新令牌。此端点只能由机密客户端调用。

有关如何在此端点调用的更多详细信息,请参阅 OAuth 2.0 Token Introspection 规格

您可以使用 HTTP 标头 Accept: application/jwt 而不是 Accept: application/json 来调用内省端点。如果是 application/jwt,响应可能包含带有完整 JWT 访问令牌的额外声明 jwt,这非常有用,特别是要内省的令牌是 轻量级访问令牌。这要求您在客户端高级设置中启用 Introspection Response 中启用支持 JWT 声明,该声明 会触发令牌内省。

2.1.1.7. 动态客户端注册端点
/realms/{realm-name}/clients-registrations/openid-connect

动态客户端注册端点用于动态注册客户端。

如需了解更多详细信息,请参阅使用 客户端注册服务 章节和 OpenID Connect Dynamic Client Registration 规格

2.1.1.8. 令牌撤销端点
/realms/{realm-name}/protocol/openid-connect/revoke

令牌撤销端点用于撤销令牌。此端点支持刷新令牌和访问令牌。在撤销刷新令牌时,也会撤销相应客户端的同意。

有关如何在此端点调用的更多详细信息,请参阅 OAuth 2.0 Token Revocation 规格

2.1.1.9. 设备授权端点
/realms/{realm-name}/protocol/openid-connect/auth/device

设备授权端点用于获取设备代码和用户代码。它可以被机密或公共客户端调用。

有关如何在此端点调用的更多详细信息,请参阅 OAuth 2.0 设备授权规格

2.1.1.10. Backchannel Authentication 端点
/realms/{realm-name}/protocol/openid-connect/ext/ciba/auth

backchannel 身份验证端点用于获取 auth_req_id,用于标识客户端发出的身份验证请求。它只能由机密客户端调用。

有关如何在此端点调用的详情,请参阅 OpenID Connect Client Initiated Backchannel Authentication Flow 规格

另请参阅 Red Hat build of Keycloak 文档(如 Client Initiated Backchannel Authentication Grant 部分) 和 Server Administration Guide 中的 Client Initiated Backchannel Authentication Grant 部分

2.2. 支持的授予类型

这部分论述了转发方可用的不同授权类型。

2.2.1. 授权代码

Authorization Code 流将用户代理重定向到红帽构建的 Keycloak。用户通过红帽构建的 Keycloak 成功进行身份验证后,会创建一个授权代码,并将用户代理重定向到应用程序。然后,应用程序使用授权代码及其凭证从红帽构建的 Keycloak 获取访问令牌、刷新令牌和 ID 令牌。

流面向 Web 应用程序,但推荐原生应用程序,包括移动应用程序,其中可以嵌入用户代理。

更多详情请参阅 OpenID Connect 规格中的 授权代码流

2.2.2. 隐式

Implicit 流的工作方式与授权代码流类似,但不返回授权代码,而是返回访问令牌和 ID 令牌。这种方法可减少额外调用交换访问令牌授权代码的需求。但是,它不包含 Refresh Token。这导致需要允许具有长到期的访问令牌;但是,这种方法并不实际,因为它很难使这些令牌无效。或者,当初始访问令牌过期后,您可以要求新的重定向来获取新的访问令牌。如果应用程序只想验证用户并处理注销本身,则 Implicit 流很有用。

您可以使用返回 Access Token 和 Authorization Code 的混合流。

需要注意的是,Implicit 流和混合流程都有潜在的安全风险,因为访问令牌可能会通过 Web 服务器日志和浏览器历史记录泄露。您可以通过对访问令牌使用简短的过期时间来缓解此问题。

如需了解更多详细信息,请参阅 OpenID Connect 规格中的 Implicit 流

根据当前 对 OAuth 2.0 安全性(RFC 9700)的最佳实践,不使用此流 SHOULD。此流将从将来的 OAuth 2.1 规范 中删除。

2.2.3. 资源所有者密码凭证

资源所有者密码凭证(称为 Direct Grant in Red Hat build of Keycloak)允许交换令牌的用户凭证。根据当前 对 OAuth 2.0 安全性(RFC 9700)的最佳实践,不需要使用这个流,首选使用替代方法,如 第 2.2.5 节 “设备授权”第 2.2.1 节 “授权代码”

使用这个流的限制包括:

  • 用户凭证公开给应用程序
  • 应用程序需要登录页面
  • 应用程序需要了解身份验证方案
  • 对身份验证流程的更改需要更改应用程序
  • 不支持身份代理或社交登录
  • 不支持流(用户自助注册、所需操作等)

此流中的安全问题包括:

  • 涉及红帽构建的 Keycloak 处理凭证
  • 增加可能会发生凭证泄漏的问题
  • 创建一个生态系统,用户信任另一个应用程序以输入其凭证,而不是红帽构建的 Keycloak

要使客户端被允许使用 Resource Owner Password Credentials 授权,客户端必须启用 Direct Access Grants Enabled 选项。

此流不包含在 OpenID Connect 中,而是 OAuth 2.0 规格的一部分。它将从将来的 OAuth 2.1 规范 中删除。

如需了解更多详细信息,请参阅 OAuth 2.0 规格中的 Resource Owner Password Credentials Grant 章节。

2.2.3.1. 使用 CURL 的示例

以下示例演示了如何为 realm master 中的一个用户获得访问令牌,用户名 user,密码 password。该示例使用机密客户端 myclient

curl \
  -d "client_id=myclient" \
  -d "client_secret=40cc097b-2a57-4c17-b36a-8fdf3fc2d578" \
  -d "username=user" \
  -d "password=password" \
  -d "grant_type=password" \
  "http://localhost:8080/realms/master/protocol/openid-connect/token"

2.2.4. 客户端凭证

当客户端(应用程序和服务)想要代表自己获得访问权限时,会使用客户端凭据,而不是代表用户的访问权限。例如,这些凭证对于通常对系统应用更改的后台服务非常有用,而不是对特定用户应用更改。

Red Hat build of Keycloak 支持客户端使用 secret 或公钥进行身份验证。

此流不包含在 OpenID Connect 中,而是 OAuth 2.0 规格的一部分。

如需了解更多详细信息,请参阅 OAuth 2.0 规格中的 客户端 凭证授予章节。

2.2.5. 设备授权

在具有有限输入功能或缺少合适的浏览器的互联网连接设备上运行的客户端使用设备授权。

  1. 红帽构建的 Keycloak 的应用程序请求提供了一个设备代码和用户代码。
  2. 红帽构建的 Keycloak 创建设备代码和用户代码。
  3. 红帽构建的 Keycloak 返回一个响应,包括设备代码和用户代码到应用程序。
  4. 应用为用户提供用户代码和验证 URI。用户访问验证 URI 以使用另一个浏览器进行身份验证。
  5. 应用程序重复轮询红帽构建的 Keycloak,直到红帽构建的 Keycloak 完成用户授权。
  6. 如果用户身份验证完成,应用程序会获取设备代码。
  7. 应用程序使用设备代码及其凭证从红帽构建的 Keycloak 获取访问令牌、刷新令牌和 ID Token。

如需了解更多详细信息,请参阅 OAuth 2.0 设备授权规格

2.2.6. Client Initiated Backchannel Authentication Grant

Client Initiated Backchannel Authentication Grant 由希望通过直接与 OpenID 提供程序通信而发起身份验证流程的客户端使用,而无需通过 OAuth 2.0 的授权代码授权等重定向。

来自红帽构建的 Keycloak 的客户端请求一个 auth_req_id,用于标识客户端发出的身份验证请求。Red Hat build of Keycloak 创建 auth_req_id。

收到这个 auth_req_id 后,这个客户端需要重复轮询红帽构建的 Keycloak,以获取来自红帽构建的 Keycloak 的访问令牌、刷新令牌和 ID Token,以便为 auth_req_id 返回。

如果客户端使用 ping 模式,则不需要重复轮询令牌端点,但可以等待红帽构建 Keycloak 向指定的客户端通知端点发送的通知。Client Notification Endpoint 可以在 Red Hat build of Keycloak Admin 控制台中配置。CIBA 规范中描述了客户端通知端点合同详情。

如需了解更多详细信息,请参阅 OpenID Connect Client Initiated Backchannel Authentication Flow 规格

另外,请参阅 Red Hat build of Keycloak 文档的其它位置,如 本指南的 Backchannel Authentication Endpoint 和 Server Administration Guide 中的 Client Initiated Backchannel Authentication Grant 部分。有关 FAPI CIBA 合规性的详情,请查看 本指南的 FAPI 部分

2.3. 红帽构建的 Keycloak 特定错误

红帽构建的 Keycloak 服务器可以在 OIDC 身份验证响应中向客户端应用程序发送错误,并带有参数 error=temporarily_unavailableerror_description=authentication_expired。当用户进行身份验证并具有 SSO 会话时,Red Hat build of Keycloak 会发送这个错误,但在当前浏览器标签页中过期身份验证,因此红帽构建的 Keycloak 服务器无法自动进行 SSO 重新身份验证,并以成功响应重定向到客户端。当客户端应用程序收到这种类型的错误时,最好立即重试身份验证,并向红帽构建的 Keycloak 服务器发送一个新的 OIDC 身份验证请求,这通常应该始终因为 SSO 会话和重定向而验证用户。如需了解更多详细信息,请参阅 服务器管理指南

2.4. 财务级 API (FAPI)支持

红帽构建的 Keycloak 使管理员更容易确保其客户端符合以下规格:

此合规性意味着红帽构建的 Keycloak 服务器将验证授权服务器的要求,这在规格中提到。红帽构建的 Keycloak 适配器没有对 FAPI 的任何特定支持,因此客户端(应用程序)一侧所需的验证可能仍然需要手动完成,或者通过其他第三方解决方案完成。

2.4.1. FAPI 客户端配置集

为确保您的客户端兼容 FAPI,您可以在您的域中配置客户端策略,如服务器管理 指南中所述,并将它们链接到 FAPI 支持的全局客户端配置集,这些配置文件可在每个域中自动可用。您可以根据您需要客户端符合哪个 FAPI 配置集,使用 fapi-1-baselinefapi-1-advanced 配置集。您还可以使用配置文件 fapi-2-security-profile,fapi-2-dpop-security-profile,fapi-2-message-signingfapi-2-dpop-message-signing 符合 FAPI 2.0 规格。

如果要使用 Pushed Authorization Request (PAR),建议您客户端同时使用 fapi-1-baseline 配置集和 fapi-1-advanced 用于 PAR 请求。具体来说,fapi-1-baseline 配置集包含 pkce-enforcer executor,这样可确保客户端使用带有安全 S256 算法的 PKCE。FAPI 高级客户端不需要此功能,除非它们使用 PAR 请求。

如果您要以 FAPI 兼容方式使用 CIBA,请确保您的客户端同时使用 fapi-1-advancedfapi-ciba 客户端配置集。需要使用 fapi-1-advanced 配置集,或者包含请求的 executor 的其他客户端配置文件,因为 fapi-ciba 配置集仅包含 CIBA 特定的 executor。当强制实施 FAPI CIBA 规格的要求时,需要额外的要求,如强制机密客户端或证书绑定访问令牌。

红帽 Keycloak 的构建符合 Open Finance Brasil financial-grade API Security Profile 1.0 实现者 Draft 3。这比 FAPI 1 高级 规格的一些要求更严格,因此可能需要更严格地配置客户端 策略 来强制实施某些要求。特别是:

  • 如果您的客户端没有使用 PAR,请确保它使用加密的 OIDC 请求对象。这可以通过使用带有启用了 Encryption Requiredsecure-request-object executor 的客户端配置文件来实现。
  • 确保对于 JWS,客户端使用 PS256 算法。对于 JWE,客户端应使用 RSA-OAEPA256GCM。这可能需要在这些算法适用 的所有客户端 设置中设置。

Red Hat build of Keycloak 与 Australia Consumer Data Right Security Profile 兼容。

如果要应用 Australia CDR 安全配置集,您需要使用 fapi-1-advanced 配置集,因为 Australia CDR 安全配置集基于 FAPI 1.0 高级安全配置集。如果您的客户端也应用 PAR,请确保客户端应用 RFC 7637 证明密钥进行代码交换(PKCE),因为澳大利亚 CDR 安全配置集需要在应用 PAR 时应用 PKCE。这可以通过使用带有 pkce-enforcer executor 的客户端配置文件来实现。

2.4.4. TLS 注意事项

随着机密信息被交换,所有交互都应通过 TLS (HTTPS)加密。此外,FAPI 规格中使用的密码套件和 TLS 协议版本有一些要求。要满足这些要求,您可以考虑配置允许的密码。此配置可以通过设置 https-protocolshttps-cipher-suites 选项来完成。红帽 Keycloak 的构建默认使用 TLSv1.3,因此可能不需要更改默认设置。但是,如果您需要因为某种原因回退到较低 TLS 版本,可能需要调整密码。如需了解更多详细信息,请参阅配置 TLS 章节。

2.5. OAuth 2.1 支持

红帽构建的 Keycloak 使管理员更容易确保其客户端符合以下规格:

此合规性意味着红帽构建的 Keycloak 服务器将验证授权服务器的要求,这在规格中提到。红帽构建的 Keycloak 适配器没有对 OAuth 2.1 的任何特定支持,因此客户端(应用程序)一侧所需的验证可能仍然需要手动完成,或者通过其他第三方解决方案完成。

2.5.1. OAuth 2.1 客户端配置集

为确保您的客户端符合 OAuth 2.1,您可以在您的域中配置客户端策略,如 服务器管理指南中所述,并将它们链接到 OAuth 2.1 支持的全局客户端配置文件中,这些配置文件可在每个域中自动可用。您可以将 oauth-2-1-for-confidential-client 配置集用于机密客户端,或使用 oauth-2-1-for-public-client 配置集用于公共客户端。

注意

OAuth 2.1 规格仍是一个草案,未来可能会改变。因此,红帽构建的 Keycloak 内置 OAuth 2.1 客户端配置集也可以更改。

注意

将 OAuth 2.1 配置集用于公共客户端时,建议使用 DPoP 预览功能,如服务器管理 指南中所述,DPoP 将访问令牌和刷新令牌与客户端的公钥部分绑定在一起。这个绑定可防止攻击者使用 stolen 令牌。

2.6. 建议

本节介绍了使用红帽构建的 Keycloak 保护应用程序时的一些建议。

2.6.1. 验证访问令牌

如果您需要手动验证红帽构建的 Keycloak 发布的访问令牌,您可以调用 Introspection Endpoint。这种方法的缺点是您必须对红帽构建的 Keycloak 服务器进行网络调用。如果您同时存在太多验证请求,这可能会较慢,并可能过载服务器。红帽构建的 Keycloak 发布访问令牌是 JSON Web 令牌(JWT) 数字签名,并使用 JSON Web 签名(JWS) 进行编码。由于它们以这种方式编码,因此您可以使用发布域的公钥在本地验证访问令牌。您可以在验证代码中硬编码域的公钥,也可以使用嵌入在 JWS 中的密钥 ID (KID) 的证书端点 查找并缓存公钥。根据您采用什么语言,有许多第三方库存在,它们可帮助您进行 JWS 验证。

2.6.2. 重定向 URI

在使用基于重定向的流时,请务必为客户端使用有效的重定向 uris。重定向 uris 应尽量具体。这特别适用于客户端(公共客户端)应用程序。无法这样做可能会导致:

  • 打开重定向 - 这使攻击者能够创建像来自您的域的 欺骗链接
  • 未授权条目 - 当用户已通过红帽构建的 Keycloak 进行身份验证时,攻击者可以使用一个公共客户端,其中 redirect uris 没有正确配置,在没有用户不了解的情况下通过重定向用户来获取访问权限。

在生产中,Web 应用始终对所有重定向 URI 使用 https。不允许重定向到 http。

还有一些特殊的重定向 URI:

http://127.0.0.1
此重定向 URI 对原生应用很有用,并允许原生应用在可用于获取授权代码的随机端口上创建 Web 服务器。此重定向 uri 允许任何端口。请注意,对于原生应用,每个 OAuth 2.0不建议使用 localhost,应改为使用 IP 字面 127.0.0.1
urn:ietf:wg:oauth:2.0:oob
如果您无法在客户端(或浏览器不可用)中启动 Web 服务器,您可以使用特殊的 urn:ietf:wg:oauth:2.0:oob redirect uri。当使用此 redirect uri 时,红帽构建的 Keycloak 会显示带有标题和页面中的代码的页面。应用可以检测浏览器标题已更改,或者用户可以手动将代码复制并粘贴到应用中。使用这个重定向 uri,用户可以使用不同的设备获取代码来粘贴到应用程序。

第 3 章 红帽构建的 Keycloak JavaScript 适配器

客户端 JavaScript 库,可用于保护 Web 应用。

红帽构建的 Keycloak 附带了一个名为 keycloak-js 的客户端 JavaScript 库,可用于保护 Web 应用程序。该适配器还附带对 Cordova 应用程序的内置支持。适配器在 covers 下使用 OpenID Connect 协议。您可以查看使用 OpenID Connect 保护应用程序和服务章节以了解 OpenID Connect 端点和功能的通用信息。

3.1. 安装

我们建议您从 NPM 安装 keycloak-js 软件包:

npm install keycloak-js

3.2. Red Hat build of Keycloak 服务器配置

有关使用客户端应用的一个重要事项是,客户端必须是一个公共客户端,因为无法将客户端凭据存储在客户端侧应用中。考虑到这一点非常重要,请确保为客户端配置的重定向 URI 是正确的,并尽可能具体。

要使用适配器,请在 Red Hat build of Keycloak Admin Console 中为您的应用程序创建一个客户端。通过将 Client 身份验证 切换为 OffCapability config 页面中使客户端变为 public。

您还需要配置 Valid Redirect URIWeb Origins。尽量具体这样做可能会导致安全漏洞。

3.3. 使用适配器

以下示例演示了如何初始化适配器。确保将传递给 Keycloak 构造器的选项替换为您配置的客户端。

import Keycloak from 'keycloak-js';

const keycloak = new Keycloak({
    url: "http://keycloak-server",
    realm: "my-realm",
    clientId: "my-app"
});

try {
    const authenticated = await keycloak.init();
    if (authenticated) {
        console.log('User is authenticated');
    } else {
        console.log('User is not authenticated');
    }
} catch (error) {
    console.error('Failed to initialize adapter:', error);
}

要进行身份验证,您可以调用 login 功能。有两个选项可使适配器自动验证。您可以将 login-requiredcheck-sso 传递给 init () 函数。

  • 如果用户登录到 Red Hat build of Keycloak,或者如果用户没有登录,则 login-required 会验证客户端。
  • 只有用户已经登录,check-sso 才会验证客户端。如果用户没有登录,浏览器将重定向到应用程序,并保持未经身份验证的操作。

您可以配置一个静默check-sso 选项。启用这个功能后,您的浏览器不会对红帽构建的 Keycloak 服务器执行完整重定向到您的应用程序,但此操作将在隐藏的 iframe 中执行。因此,您的应用程序资源只会由浏览器加载并解析,即当应用程序初始化时,在从 Red Hat build of Keycloak 到您的应用程序后不会再次加载并解析。当 SPAs (单页应用程序)时,这种方法特别有用。

要启用 静默 check-sso,您可以在 init 方法中提供一个 silentCheckSsoRedirectUri 属性。确保此 URI 是应用程序中的有效端点,必须将其配置为红帽构建的 Keycloak 管理控制台中客户端的有效重定向:

await keycloak.init({
    onLoad: 'check-sso',
    silentCheckSsoRedirectUri: `${location.origin}/silent-check-sso.html`
});

在成功检查身份验证状态并从红帽构建的 Keycloak 服务器检索令牌后,silent check-sso redirect uri 的页面会在 iframe 中加载。它没有其他任务,而不是将接收的令牌发送到主应用程序,并且只应类似如下:

<!doctype html>
<html>
<body>
    <script>
        parent.postMessage(location.href, location.origin);
    </script>
</body>
</html>

请记住,此页面必须由应用程序在 silentCheckSsoRedirectUri 中指定的位置提供,且不是 适配器的一部分。

警告

在某些现代浏览器中,静默check-sso 会有一些限制。请参阅 带有跟踪保护部分的现代浏览器

要在 Load 上启用 login-required 设置为 login-required,并传递给 init 方法:

await keycloak.init({
    onLoad: 'login-required'
});

在验证了用户后,应用程序可以通过在 Authorization 标头中包含 bearer 令牌来向由红帽构建的 Keycloak 保护的 RESTful 服务发出请求。例如:

async function fetchUsers() {
    const response = await fetch('/api/users', {
        headers: {
            accept: 'application/json',
            authorization: `Bearer ${keycloak.token}`
        }
    });

    return response.json();
}

需要注意的是,默认情况下访问令牌具有较短的生命周期过期,因此您可能需要在发送请求前刷新访问令牌。您可以通过调用 updateToken () 方法刷新此令牌。此方法返回一个 Promise,这只有在令牌被成功刷新时才会轻松调用服务,并在用户没有刷新时向用户显示错误。例如:

try {
    await keycloak.updateToken(30);
} catch (error) {
    console.error('Failed to refresh token:', error);
}

const users = await fetchUsers();
注意

访问和刷新令牌都存储在内存中,且不会保留在任何类型的存储中。因此,这些令牌应该永远不会被保留,以防止劫持攻击。

3.4. 会话状态 iframe

默认情况下,适配器会创建一个隐藏的 iframe,用于检测是否发生 Single-Sign Out。这个 iframe 不需要任何网络流量。相反,可通过查看特殊状态 Cookie 来检索状态。通过在传递给 init () 方法的选项中设置 checkLoginIframe: false 来禁用这个功能。

您不应该依赖直接查看这个 Cookie。其格式可以更改,它也与红帽构建的 Keycloak 服务器的 URL 相关联,而不是您的应用程序。

警告

在某些现代浏览器中,会话状态 iframe 功能受到限制。请参阅 带有跟踪保护部分的现代浏览器

3.5. 隐式和混合流程

默认情况下,适配器使用 Authorization Code 流。

在这个版本中,红帽构建的 Keycloak 服务器会向应用程序返回授权代码,而不是身份验证令牌。JavaScript 适配器在浏览器重定向到应用后交换访问令牌的代码和刷新令牌。

红帽构建的 Keycloak 还支持 Implicit 流,在成功通过红帽构建的 Keycloak 进行身份验证后立即发送访问令牌。此流的性能可能比标准流更好,因为不存在额外的请求来交换令牌的代码,但访问令牌过期时有影响。

但是,在 URL 片段中发送访问令牌可能是安全漏洞。例如,令牌可以通过 Web 服务器日志和浏览器历史记录泄漏。

要启用隐式流,您可以在红帽构建的 Keycloak 管理控制台中为客户端启用 Implicit Flow Enabled 标志。您还可以将参数 以及值 隐式 传递给 init 方法:

await keycloak.init({
    flow: 'implicit'
})

请注意,只有访问令牌会被提供,且不存在刷新令牌。这意味着,在访问令牌过期后,应用程序必须再次重定向到红帽构建的 Keycloak 来获取新的访问令牌。

红帽构建的 Keycloak 还支持 混合 流。

此流程要求客户端在 Admin 控制台中同时启用了 Standard FlowImplicit Flow。红帽构建的 Keycloak 服务器随后将代码和令牌都发送到您的应用程序。访问令牌可以立即使用,而代码可以交换来访问和刷新令牌。与隐式流类似,混合流对性能很好,因为访问令牌会立即可用。但是,令牌仍然在 URL 中发送,之前提到的安全漏洞可能仍然适用。

混合流中的一个优点是,应用程序可以使用刷新令牌。

对于混合流,您需要将带有值 hybrid 的参数 传递给 init 方法:

await keycloak.init({
    flow: 'hybrid'
});

3.6. 带有 Cordova 的混合应用程序

红帽构建的 Keycloak 支持通过 Apache Cordova 开发混合移动应用程序。该适配器有两种模式: cordovacordova-native

默认为 cordova,如果未明确配置适配器类型,并且存在 window.cordova,则适配器会自动选择。登录时,它会打开一个 InApp 浏览器,允许用户与红帽构建的 Keycloak 交互,之后通过重定向到 http://localhost 来返回到应用程序。由于此行为,您可以在 Admin Console 的客户端配置部分中将此 URL 列为有效的 redirect-uri。

虽然这个模式易于设置,但也有一些缺点:

  • InApp-Browser 是嵌入在应用程序中的浏览器,不是手机的默认浏览器。因此,它具有不同的设置,存储的凭据将不可用。
  • InApp-Browser 可能也较慢,特别是在渲染更复杂的主题时。
  • 在使用此模式前需要考虑安全问题,例如,应用程序可以访问用户的凭证,因为它完全控制浏览器的登录页面,因此不允许其在不受信任的应用程序中使用。

备用模式是"cordova-native",采用不同的方法。它使用系统浏览器打开登录页面。用户通过身份验证后,浏览器使用特殊 URL 重定向到应用。从那里,红帽构建的 Keycloak 适配器可以通过从 URL 读取代码或令牌来完成登录。

您可以通过将适配器类型 cordova-native 传递给 init () 方法来激活原生模式:

await keycloak.init({
    adapter: 'cordova-native'
});

这个适配器需要两个额外的插件:

链接到应用程序的技术详情因每个平台而异,需要特殊设置。如需了解更多信息,请参阅 deeplinks 插件文档中的 Android 和 iOS 部分。

对于打开应用程序,存在不同类型的链接:

虽然前者更容易设置,而且往往能够更可靠地工作,但后者会提供额外的安全性,因为它们是唯一的,并且只有域的所有者才能注册它们。custom-URLs 在 iOS 中已弃用。为获得最佳可靠性,我们建议您将通用链接与使用 custom-url 链接的回退站点结合使用。

另外,我们建议以下步骤来提高与适配器的兼容性:

  • iOS 上的通用链接看似可以更可靠地工作,并将 response-mode 设置为 query
  • 要防止 Android 在重定向时打开一个新的应用程序实例,请在 config.xml 中添加以下片断:
<preference name="AndroidLaunchMode" value="singleTask" />

3.7. 自定义适配器

在某些情况下,您可能需要在默认不支持的环境中运行适配器,如 Capacitor。要在这些环境中使用 JavasScript 客户端,您可以传递自定义适配器。例如,第三方库可以提供这样的适配器,以便可靠地运行适配器:

import Keycloak from 'keycloak-js';
import KeycloakCapacitorAdapter from 'keycloak-capacitor-adapter';

const keycloak = new Keycloak({
    url: "http://keycloak-server",
    realm: "my-realm",
    clientId: "my-app"
});

await keycloak.init({
    adapter: KeycloakCapacitorAdapter,
});

这个特定软件包不存在,但提供了一个很好的例子,说明如何将此类适配器传递给客户端。

也可以自行进行适配器,因此您必须实现 KeycloakAdapter 接口中描述的方法。例如,以下 TypeScript 代码可确保正确实施所有方法:

import Keycloak, { KeycloakAdapter } from 'keycloak-js';

// Implement the 'KeycloakAdapter' interface so that all required methods are guaranteed to be present.
const MyCustomAdapter: KeycloakAdapter = {
    async login(options) {
        // Write your own implementation here.
    }

    // The other methods go here...
};

const keycloak = new Keycloak({
    url: "http://keycloak-server",
    realm: "my-realm",
    clientId: "my-app"
});

await keycloak.init({
    adapter: MyCustomAdapter,
});

自然情况下,您还可以在没有 TypeScript 的情况下进行此操作,但确保正确实施接口将完全保留您。

3.8. 带有跟踪保护的现代浏览器

在一些浏览器的最新版本中,应用各种 Cookie 策略以防止由第三方跟踪用户,如 Chrome 中的 SameSite 或完全阻止第三方 Cookie。这些策略可能会加大限制并被其他浏览器使用。最终,第三方上下文中的 Cookie 可能会完全不受支持且被浏览器阻止。因此,受影响的适配器功能最终可能会被弃用。

该适配器依赖于 Session Status iframe、静默 check-sso 以及部分常规(非静默) check-sso。这些功能具有有限的功能,或者根据浏览器与 Cookie 的限制而完全禁用。适配器会尝试检测此设置并相应地响应。

3.8.1. 带有 "SameSite=Lax default" 策略的浏览器

如果在红帽构建的 Keycloak 端以及应用程序端配置了 SSL / TLS 连接,则支持所有功能。例如,Chrome 会受到影响,从版本 84 开始。

3.8.2. 带有 Blocked Third-Party Cookies 的浏览器

不支持 Session Status iframe,如果适配器检测到此类浏览器行为,则会自动禁用。这意味着适配器无法对单点登录检测使用会话 Cookie,且必须完全依赖令牌。因此,当用户在另一个窗口中退出时,在应用程序尝试刷新访问令牌前,不会退出使用适配器的应用程序。因此,请考虑将访问令牌生命周期设置为相对较短的时间,以便尽快检测到注销。如需了解更多详细信息,请参阅 会话和令牌超时

不支持静默 check-sso ,会默认回退到常规(非静默)check-sso。通过在传递给 init 方法的选项中设置 silentCheckSsoFallback: false 来更改此行为。在这种情况下,如果检测到限制性浏览器行为,check-sso 会被完全禁用。

常规 check-sso 也会受到影响。因为 Session Status iframe 不支持,在适配器初始化以检查用户的登录状态时,必须进行到红帽构建的 Keycloak 的额外重定向。当 iframe 用于确定用户是否登录时,这个检查与标准行为不同,并且仅在用户注销时才执行重定向。

受影响的浏览器示例是从版本 13.1 开始的 Safari。

3.9. API 参考

3.9.1. Constructor

构造适配器的建议方法是直接传递配置:

new Keycloak({
    url: "http://keycloak-server",
    realm: "my-realm",
    clientId: "my-app"
});

另外,也可以将 URL 传递给包含配置的 JSON 文件:

new Keycloak("http://your-app/adapter-config.json");

JSON 配置文件必须包括以下字段:

{
    "auth-server-url": "https://auth.example.com",
    "realm": "my-realm",
    "resource": "my-app"
}

请注意,使用 JSON 配置文件将在初始化前触发额外的请求,因此不建议提高性能。

3.9.2. Properties

authenticated
如果用户通过身份验证,则为 true,否则为 false
token
可以在请求中的 Authorization 标头中向服务发送 base64 编码令牌。
tokenParsed
解析的令牌作为一个 JavaScript 对象。
subject
用户 ID。
idToken
base64 编码的 ID 令牌。
idTokenParsed
解析的 id 令牌作为 JavaScript 对象。
realmAccess
与令牌关联的 realm 角色。
resourceAccess
与令牌关联的资源角色。
refreshToken
可用于检索新令牌的 base64 编码刷新令牌。
refreshTokenParsed
解析的刷新令牌作为 JavaScript 对象。
timeSkew
浏览器时间和Keycloak 服务器构建之间的预计时间差(以秒为单位)。这个值只是一个估算,但在确定令牌是否过期时足够准确。
responseMode
在 init 中传递的响应模式(默认值为片段)。
在 init 中传递的流程。
adapter

允许您覆盖重定向和其他浏览器相关功能的方式由该库处理。可用选项:

  • "default" - 库使用浏览器 api 进行重定向(这是默认设置)
  • "Cordova" - 库会尝试使用 InAppBrowser cordova 插件加载 keycloak 登录/注册页面(当库在 cordova 生态系统中工作时会自动使用)
  • "Cordova-native" - 库尝试使用 BrowserTabs cordova 插件,使用手机的系统浏览器打开登录和注册页面。这需要额外的设置才能重新重定向到应用程序(请参阅 第 3.6 节 “带有 Cordova 的混合应用程序”)。
  • "custom" - 允许您实施自定义适配器(仅适用于高级用例)
responseType
发送到带有登录请求的红帽构建的 Keycloak 的响应类型。这根据初始化过程中使用的流值决定,但可通过设置此值来覆盖。

3.9.3. Methods

init (options)

调用用于初始化适配器。

选项是一个对象,其中:

  • useNonce - 添加一个加密,以验证身份验证响应是否与请求匹配(默认为 true)。
  • onLoad - 指定要在负载时执行的操作。支持的值有 login-requiredcheck-sso
  • redirecturi - 指定在登录或注销后重定向到的默认 uri。目前,这个适配器 'cordova-native' 和 'default' 支持。
  • silentCheckSsoRedirectUri - 如果 onLoad 设为 'check-sso',则为 silent 身份验证检查设置重定向 uri。
  • silentCheckSsoFallback - 当浏览器不支持 静默 check-sso 时(默认为 true),启用回到普通的 check-sso
  • token - 为令牌设置初始值。
  • refreshToken - 为刷新令牌设置初始值。
  • idToken - 为 id 令牌设置初始值(仅与 token 或 refreshToken 一同使用)。
  • Scope - 将默认 scope 参数设置为红帽构建的 Keycloak 登录端点。使用以空格分隔的范围列表。它们通常引用 特定客户端中定义的客户端范围。请注意,范围 openid 始终将添加到适配器的范围列表中。例如,如果您输入范围选项 地址电话,则红帽构建的 Keycloak 的请求将包含 scope 参数 scope=openid 地址电话。请注意,如果 login () 选项明确指定范围,则此处指定的默认范围会被覆盖。
  • timeSkew - 以秒为单位为本地时间和红帽构建 Keycloak 服务器设置 skew 的初始值(仅与令牌或 refreshToken 一起)。
  • checkLoginIframe - 设置为启用/禁用监控登录状态(默认为 true)。
  • checkLoginIframeInterval - 设置检查登录状态的时间间隔(默认为 5 秒)。
  • responseMode - 在登录请求时将 OpenID Connect 响应模式发送到红帽构建的 Keycloak 服务器。有效值为 queryfragment。默认值为 片段,这意味着在成功身份验证后,红帽构建 Keycloak 会重定向到 URL 片段中添加 OpenID Connect 参数的 JavaScript 应用程序。这通常更安全,建议使用它而不是 query
  • 流 - 设置 OpenID Connect 流。有效值为 标准隐式 或混合
  • enableLogging - 启用来自 Keycloak 到控制台的日志记录消息(默认为 false)。
  • pkceMethod - 要使用的概念验证(PKCE)。配置这个值可启用 PKCE 机制。可用选项:

    • "S256" - 基于 SHA256 的 PKCE 方法(默认)
    • false - PKCE 被禁用。
  • messageReceiveTimeout - 以毫秒为单位设置等待 Keycloak 服务器消息响应的超时。这用于,例如,在第三方 Cookie 检查期间等待消息。默认值为 10000。
  • locale - 当Load 为 'login-required' 时,请在 OIDC 1.0 规格的 3.1.2.1 部分中设置 'ui_locales' 查询参数。

返回初始化完成后解析的承诺。

login (options)

重定向到登录表单,返回 Promise。

options 是一个可选对象,其中:

  • redirecturi - 指定在登录后要重定向到的 uri。
  • prompt - 这个参数允许在红帽构建的 Keycloak 服务器端自定义登录流。例如,如果值为 login,则强制显示登录屏幕。当客户端具有 Consent required 时,或强制显示对值同意 的同意 屏幕。最后,可以使用值 none 来确保用户未显示登录屏幕,这对在用户已通过身份验证后检查 SSO 非常有用(这与上述值 check-sso 相关)。
  • maxAge - 只有用户已通过身份验证时才使用。指定自用户身份验证以来的最长时间。如果用户已经验证了比 maxAge 更长的时间,则忽略 SSO,并且需要再次验证身份。
  • loginHint - 用于预先填充登录表单上的用户名/电子邮件字段。
  • Scope - 使用这个特定登录的不同值覆盖 init 中配置的范围。
  • idpHint - 用于告知红帽构建的 Keycloak,跳过显示登录页面并自动重定向到指定的身份提供程序。身份提供程序文档中的更多信息
  • acr - 包含有关 acr 声明的信息,该声明将在 claim 参数内发送到红帽构建的 Keycloak 服务器。典型的用法用于逐步身份验证。使用 { values: ["silver", "gold"], essential: true }.如需了解更多详细信息,请参阅 OpenID Connect 规格和步骤 身份验证文档
  • acrValues - 生成 acr_values 参数,该参数引用身份验证上下文类,并允许客户端声明所需的保证级别要求,如身份验证机制。请参阅 OpenID Connect MODRNA Authentication Profile 1.0 中的第 4 节:cr_values 请求值和保证级别
  • action - 如果值是 register,用户会被重定向到注册页面。如需了解更多详细信息 ,请参阅客户端请求的注册部分。如果值为 UPDATE_PASSWORD 或另一个支持的所需操作,则用户将重定向到重置密码页面或其他必要的操作页面。但是,如果用户未通过身份验证,则用户将发送到登录页面并在身份验证后重定向。如需了解更多详细信息,请参阅应用程序初始操作部分
  • locale - 在 OIDC 1.0 规格的 3.1.2.1 部分中符合部分 3.1.2.1 设置 'ui_locales' 查询参数。
  • cordovaOptions - 指定传递给 Cordova in-app-browser (如果适用)的参数。选择 hiddenlocation 不受这些参数的影响。所有可用选项都在 https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/ 中定义。用法示例 :{ zoom: "no", hardwareback: "yes" };

createLoginUrl(options)

返回包含 URL 的 Promise,以登录表单。

选项是一个可选对象,它支持与功能 登录 相同的选项。

logout (options)

重定向至注销。

选项是一个对象,其中:

  • redirecturi - 指定在注销后要重定向到的 uri。

createLogoutUrl(options)

返回 URL 以注销用户。

选项是一个对象,其中:

  • redirecturi - 指定在注销后要重定向到的 uri。

register (options)

重定向至注册表单。带有选项操作登录的快捷方式 = 'register'

选项与登录方法相同,但 'action' 设置为 'register'

createRegisterUrl(options)

返回包含 url 的 Promise 进行注册页面。带有选项操作的 createLoginUrl 的快捷方式 = 'register'

对于 createLoginUrl 方法,选项与 createLoginUrl 方法相同,但 'action' 设置为 'register'

accountManagement()

重定向到帐户控制台。

createAccountUrl(options)

将 URL 返回到帐户控制台。

选项是一个对象,其中:

  • redirecturi - 指定在重定向到应用程序时要重定向到的 uri。

hasRealmRole(role)

如果令牌具有给定的 realm 角色,则返回 true。

hasResourceRole (role, resource)

如果令牌具有资源的给定角色(如果未指定 clientId,则使用资源是可选的),则返回 true。

loadUserProfile()

加载 users 配置文件。

返回使用配置集解析的承诺。

例如:

try {
    const profile = await keycloak.loadUserProfile();
    console.log('Retrieved user profile:', profile);
} catch (error) {
    console.error('Failed to load user profile:', error);
}

isTokenExpired(minValidity)

如果令牌在过期前保留的时间少于 minValidity 秒,则返回 true (如果未指定 0,则返回有效)。

updateToken(minValidity)

如果令牌在 minValidity 秒内过期(如果未指定 5,则使用 minValidity 是可选的),则会刷新令牌。如果 -1 作为 minValidity 传递,则会强制刷新令牌。如果启用了会话状态,也会检查会话状态。

返回带有布尔值解析的承诺,指示令牌是否已刷新。

例如:

try {
    const refreshed = await keycloak.updateToken(5);
    console.log(refreshed ? 'Token was refreshed' : 'Token is still valid');
} catch (error) {
    console.error('Failed to refresh the token:', error);
}

clearToken()

清除身份验证状态,包括令牌。如果应用程序检测到会话已过期,例如更新令牌失败,这很有用。

调用此操作会导致调用AuthLogout 回调监听程序。

3.9.4. 回调事件

适配器支持为特定事件设置回调监听程序。请记住,必须在调用 init () 方法前设置它们。

例如:

keycloak.onAuthSuccess = () => console.log('Authenticated!');

可用的事件有:

  • 当适配器 初始化时,在Ready (authenticated) - 调用。
  • 当用户被成功验证时,在AuthSuccess - 调用。
  • 如果身份验证过程中出现错误,则 onAuthError - Called。
  • 当令牌被刷新时,在 AuthRefreshSuccess- Called 上 调用。
  • 如果在尝试刷新令牌时出现错误,则 onAuthRefreshError - Called。
  • 当用户注销时(仅在启用会话状态 iframe 或 Cordova 模式时,才会调用 AuthLogout - Called)。
  • 当访问令牌过期时,onTokenExpired - Called。如果刷新令牌可用,可以通过 updateToken 刷新令牌,或者在没有(带有隐式流)的情况下,您可以重定向到登录屏幕来获取新的访问令牌。

第 4 章 Red Hat build of Keycloak Node.js adapter

Node.js 适配器,以保护服务器端 JavaScript 应用程序。

红帽构建的 Keycloak 提供在 Connect 基础上构建的 Node.js 适配器,以保护服务器端 JavaScript 应用 - 目标足以与 Express.js 等框架集成。适配器在 covers 下使用 OpenID Connect 协议。您可以查看使用 OpenID Connect 保护应用程序和服务章节以了解 OpenID Connect 端点和功能的通用信息。

要使用 Node.js 适配器,首先需要在 Red Hat build of Keycloak Admin Console 中为您的应用程序创建一个客户端。适配器支持公共、机密和 bearer 访问类型。要选择哪个选项取决于用例场景。

创建客户端后,点右上角的 Action,然后选择 Download adapter config。对于 Format,选择 Keycloak OIDC JSON 并点 Download。下载的 keycloak.json 文件位于项目的根目录中。

4.1. 支持的 Node.js 版本

Node.js 适配器努力与 Node.js 项目 主动支持的版本 兼容。在以后的版本中可能会丢弃对 被视为生命周期结束生命周期的 Node.js 版本的支持,且不会被视为需要主版本的破坏更改。

4.2. 安装

假设您已安装了 Node.js,请为您的应用程序创建一个文件夹:

mkdir myapp && cd myapp

使用 npm init 命令为您的应用程序创建 package.json。现在,在依赖项列表中添加 Keycloak 连接适配器的红帽构建:

    "dependencies": {
        "keycloak-connect": "file:keycloak-connect-999.0.0-SNAPSHOT.tgz"
    }

4.3. 使用方法

实例化 Keycloak 类
Keycloak 类为配置和集成应用程序提供了一个中央点。最简单的创建涉及任何参数。

在项目的根目录中,创建名为 server.js 的文件并添加以下代码:

    const session = require('express-session');
    const Keycloak = require('keycloak-connect');

    const memoryStore = new session.MemoryStore();
    const keycloak = new Keycloak({ store: memoryStore });

安装 express-session 依赖项:

    npm install express-session

要启动 server.js 脚本,请在 package.json 的"scripts"部分中添加以下内容:

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "node server.js"
    },

现在,我们可以使用以下命令运行我们的服务器:

    npm run start

默认情况下,这将找到名为 keycloak.json 的文件以及应用程序的主可执行文件(在 root 文件夹中),以初始化红帽构建的 Keycloak 特定设置,如公钥、域名、各种 URL。

在这种情况下,需要红帽构建的 Keycloak 部署,以便您可以访问红帽构建的 Keycloak 管理控制台。

部署 Admin Console 后,我们已准备好通过访问红帽构建的 Keycloak Admin Console → 客户端(左侧边栏) → 选择您的 client → Installation → Format Option → Keycloak OIDC JSON → Download 来获取 keycloak.json 文件。

将下载的文件粘贴到项目的根目录上。

使用此方法实例化会导致所有合理的默认值。另外,也可提供配置对象,而不是 keycloak.json 文件:

    const kcConfig = {
        clientId: 'myclient',
        bearerOnly: true,
        serverUrl: 'http://localhost:8080',
        realm: 'myrealm',
        realmPublicKey: 'MIIBIjANB...'
    };

    const keycloak = new Keycloak({ store: memoryStore }, kcConfig);

应用程序也可以使用以下方法将用户重定向到首选身份提供程序:

    const keycloak = new Keycloak({ store: memoryStore, idpHint: myIdP }, kcConfig);
配置 Web 会话存储
如果要使用 Web 会话来管理服务器端状态进行身份验证,您需要使用至少一个 store 参数初始化 Keycloak (…​),传递 express-session 使用的实际会话存储。
    const session = require('express-session');
    const memoryStore = new session.MemoryStore();

    // Configure session
    app.use(
      session({
        secret: 'mySecret',
        resave: false,
        saveUninitialized: true,
        store: memoryStore,
      })
    );

    const keycloak = new Keycloak({ store: memoryStore });
传递自定义范围值
默认情况下,范围值 openid 作为查询参数传递给红帽构建的 Keycloak 登录 URL,但您可以添加额外的自定义值:
    const keycloak = new Keycloak({ scope: 'offline_access' });

4.4. 安装中间件

实例化后,将中间件安装到支持连接的应用程序中:

为此,首先我们必须安装 Express:

    npm install express

然后,在我们的项目中需要 Express,如下所示:

    const express = require('express');
    const app = express();

并在 Express 中配置 Keycloak 中间件,方法是在以下代码中添加:

    app.use( keycloak.middleware() );

最后,通过将以下代码添加到 main.js,使我们的服务器设置为在端口 3000 上侦听 HTTP 请求:

    app.listen(3000, function () {
        console.log('App listening on port 3000');
    });

4.5. 配置代理

如果应用程序在终止 SSL 连接 Express 的代理后面运行,则必须根据 代理指南中的表达 来配置。使用不正确的代理配置可能会导致生成无效的重定向 URI。

配置示例:

    const app = express();

    app.set( 'trust proxy', true );

    app.use( keycloak.middleware() );

4.6. 保护资源

简单身份验证
要强制在访问资源前必须验证用户,只需使用 no-argument 版本 keycloak.protect ()
    app.get( '/complain', keycloak.protect(), complaintHandler );
基于角色的授权
使用当前应用程序的 application 角色保护资源:
    app.get( '/special', keycloak.protect('special'), specialHandler );

使用不同应用程序的应用程序角色 保护资源

    app.get( '/extra-special', keycloak.protect('other-app:special'), extraSpecialHandler );

使用 realm 角色保护资源:

    app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
基于资源的授权
基于资源的授权允许您根据 Keycloak 中定义的一组策略来保护资源及其特定方法/操作,从而外部化来自应用程序的授权。这可以通过公开一个 keycloak.enforcer 方法来实现,您可以使用它来保护资源 ClusterClaim
    app.get('/apis/me', keycloak.enforcer('user:profile'), userProfileHandler);

keycloak-enforcer 方法以两种模式运行,具体取决于 response_mode 配置选项的值。

    app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), userProfileHandler);

如果将 response_mode 设为 token,则代表发送到应用程序的 bearer 令牌从服务器获取权限。在这种情况下,Keycloak 签发一个新的访问令牌,并带有服务器授予的权限。如果服务器没有以预期权限响应令牌,则请求将被拒绝。使用此模式时,您应该可以从请求获取令牌,如下所示:

    app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), function (req, res) {
        const token = req.kauth.grant.access_token.content;
        const permissions = token.authorization ? token.authorization.permissions : undefined;

        // show user profile
    });

当应用程序使用会话时,且您想要缓存来自服务器的以前的决策,并自动处理刷新令牌,首选这个模式。此模式对于充当客户端和资源服务器的应用特别有用。

如果将 response_mode 设为 permissions (默认模式),服务器仅返回授予权限的列表,而不发出新的访问令牌。除了不发布新令牌外,此方法还通过 请求 公开服务器授予权限,如下所示:

    app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'permissions'}), function (req, res) {
        const permissions = req.permissions;

        // show user profile
    });

无论使用的 response_mode 是什么,keycloak.enforcer 方法首先会尝试检查发送到应用程序的 bearer 令牌中的权限。如果 bearer 令牌已执行预期权限,则不需要与服务器交互来获取决策。当您的客户端能够在访问受保护的资源之前从具有预期权限的服务器获取访问令牌时,这特别有用,因此他们可以使用 Keycloak 授权服务(如增量授权)提供的一些功能,并在 keycloak.enforcer 强制访问资源时,避免对服务器进行额外的请求。

默认情况下,策略强制器将使用定义为应用程序的 client_id (例如,通过 keycloak.json)来引用支持 Keycloak 授权服务中的客户端。在这种情况下,客户端不能是公共的,它实际上是一个资源服务器。

如果您的应用程序充当公共客户端(frontend)和资源服务器(backend),您可以使用以下配置使用您要强制的策略在 Keycloak 中引用不同的客户端:

      keycloak.enforcer('user:profile', {resource_server_id: 'my-apiserver'})

建议您在 Keycloak 中使用不同的客户端来代表您的前端和后端。

如果您要使用 Keycloak 授权服务启用了保护的应用程序,且已在 keycloak.json 中定义了客户端凭证,您可以将额外的声明推送到服务器,并将其提供给您的策略,以便做出决策。为此,您可以定义一个 claims 配置选项,它需要一个会返回一个带有您要推送的声明的 JSON 的函数

      app.get('/protected/resource', keycloak.enforcer(['resource:view', 'resource:write'], {
          claims: function(request) {
            return {
              "http.uri": ["/protected/resource"],
              "user.agent": // get user agent  from request
            }
          }
        }), function (req, res) {
          // access granted

有关如何将 Keycloak 配置为保护您的应用程序资源的更多详细信息,请参阅 授权服务指南

高级授权
要保护基于 URL 本身的部分的资源,假设每个部分都存在角色:
    function protectBySection(token, request) {
      return token.hasRole( request.params.section );
    }

    app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );

高级登录配置:

默认情况下,所有未授权的请求都会被重定向到红帽构建的 Keycloak 登录页面,除非您的客户端是 bearer-only。但是,机密或公共客户端可以同时托管可浏览和 API 端点。要防止未经身份验证的 API 请求重定向并返回 HTTP 401,您可以覆盖 redirectToLogin 功能。

例如,此覆盖会检查 URL 是否包含 /api/ 并禁用登录重定向:

    Keycloak.prototype.redirectToLogin = function(req) {
    const apiReqMatcher = /\/api\//i;
    return !apiReqMatcher.test(req.originalUrl || req.url);
    };

4.7. 其他 URL

显式用户触发的注销
默认情况下,中间件会捕获对 /logout 的调用,以通过红帽构建以 Keycloak 为中心的注销工作流发送用户。这可以通过在 middleware() 调用中指定 logout 配置参数来更改:
    app.use( keycloak.middleware( { logout: '/logoff' } ));

当调用 user-triggered logout 时,可以传递查询参数 redirect_url

https://example.com/logoff?redirect_url=https%3A%2F%2Fexample.com%3A3000%2Flogged%2Fout

然后,这个参数用作 OIDC 注销端点的重定向 URL,用户将重定向到 https://example.com/logged/out

Red Hat build of Keycloak Admin Callbacks
另外,中间件支持来自红帽构建的 Keycloak 控制台的回调来注销单个会话或所有会话。默认情况下,这些类型的管理回调相对于 / 的根 URL 发生,但可以通过向 middleware () 调用提供 admin 参数来更改:
    app.use( keycloak.middleware( { admin: '/callbacks' } );

4.8. 完整示例

使用 Node.js 适配器用法的完整示例可在 Node.js 的 Keycloak 快速入门中找到

4.9. 升级 Node.js 适配器

要升级已复制到 web 应用程序的 Node.js 适配器,请执行以下步骤。

流程

  1. 下载新的适配器归档。
  2. 删除现有的 Node.js 适配器目录
  3. 将更新的文件解压缩到其位置
  4. 在应用程序的 package.json 中更改 keycloak-connect 的依赖项

第 5 章 配置 mod_auth_openidc Apache HTTPD 模块

使用红帽构建的 Keycloak 配置 mod_auth_openidc Apache 模块。

警告

Red Hat build of Keycloak 不提供对 mod_auth_openidc 的任何官方支持。以下说明是最佳的,可能不是最新的。为了获得更多详细信息,我们建议您使用官方的 mod_auth_openidc 文档。

mod_auth_openidc 是 OpenID Connect 的 Apache HTTP 插件。如果您的语言/环境支持使用 Apache HTTPD 作为代理,您可以使用 mod_auth_openidc 来保护带有 OpenID Connect 的 Web 应用。这个模块的配置超出了本文档的范围。有关配置的更多详细信息,请参阅 mod_auth_openidc GitHub 存储库。

要配置 mod_auth_openidc,您需要

  • client_id。
  • client_secret。
  • redirect_uri 到您的应用程序。
  • Red Hat build of Keycloak openid-configuration url
  • mod_auth_openidc 特定的 Apache HTTPD 模块配置.

示例配置示例类似如下:

LoadModule auth_openidc_module modules/mod_auth_openidc.so

ServerName ${HOSTIP}

<VirtualHost *:80>

    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html

    #this is required by mod_auth_openidc
    OIDCCryptoPassphrase a-random-secret-used-by-apache-oidc-and-balancer

    OIDCProviderMetadataURL ${KC_ADDR}/realms/${KC_REALM}/.well-known/openid-configuration

    OIDCClientID ${CLIENT_ID}
    OIDCClientSecret ${CLIENT_SECRET}
    OIDCRedirectURI http://${HOSTIP}/${CLIENT_APP_NAME}/redirect_uri

    # maps the preferred_username claim to the REMOTE_USER environment variable
    OIDCRemoteUserClaim preferred_username

    <Location /${CLIENT_APP_NAME}/>
        AuthType openid-connect
        Require valid-user
    </Location>
</VirtualHost>

有关如何配置 mod_auth_openidc 的更多信息,请参阅 mod_auth_openidc 项目页面。

使用红帽构建的 Keycloak SAML Galleon 功能包来保护 WildFly 和 EAP 中的应用程序。

SAML 适配器作为 wildfly 29 或更高版本的 Galleon 功能软件包发布。WildFly 文档中有关 主题的更多详细信息。为 JBoss EAP 8 GA 提供相同的选项。

有关如何将 Keycloak 与最新 Wildfly/EAP 上运行的 JakartaEE 应用程序集成,请查看 Keycloak Quickstart GitHub Repository 中的 servlet-saml-service-provider Jakarta 文件夹。

6.1. 安装

功能软件包的置备是分别使用 wildfly-maven-pluginwildfly-jar-maven-plugineap-maven-plugin 进行的。

6.1.1. 使用 wildfly maven 插件的置备示例

<plugin>
    <groupId>org.wildfly.plugins</groupId>
    <artifactId>wildfly-maven-plugin</artifactId>
    <version>5.0.0.Final</version>
    <configuration>
        <feature-packs>
            <feature-pack>
                <location>wildfly@maven(org.jboss.universe:community-universe)#32.0.1.Final</location>
            </feature-pack>
            <feature-pack>
                <groupId>org.keycloak</groupId>
                <artifactId>keycloak-saml-adapter-galleon-pack</artifactId>
                <version>26.4.7</version>
            </feature-pack>
        </feature-packs>
        <layers>
            <layer>core-server</layer>
            <layer>web-server</layer>
            <layer>jaxrs-server</layer>
            <layer>datasources-web-server</layer>
            <layer>webservices</layer>
            <layer>keycloak-saml</layer>
            <layer>keycloak-client-saml</layer>
            <layer>keycloak-client-saml-ejb</layer>
        </layers>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>package</goal>
            </goals>
        </execution>
    </executions>
</plugin>

6.1.2. 使用 wildfly jar maven 插件进行调配的示例

<plugin>
    <groupId>org.wildfly.plugins</groupId>
    <artifactId>wildfly-jar-maven-plugin</artifactId>
    <version>11.0.2.Final</version>
    <configuration>
        <feature-packs>
            <feature-pack>
                <location>wildfly@maven(org.jboss.universe:community-universe)#32.0.1.Final</location>
            </feature-pack>
            <feature-pack>
                <groupId>org.keycloak</groupId>
                <artifactId>keycloak-saml-adapter-galleon-pack</artifactId>
                <version>26.4.7</version>
            </feature-pack>
        </feature-packs>
        <layers>
            <layer>core-server</layer>
            <layer>web-server</layer>
            <layer>jaxrs-server</layer>
            <layer>datasources-web-server</layer>
            <layer>webservices</layer>
            <layer>keycloak-saml</layer>
            <layer>keycloak-client-saml</layer>
            <layer>keycloak-client-saml-ejb</layer>
        </layers>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>package</goal>
            </goals>
        </execution>
    </executions>
</plugin>

6.1.3. 使用 EAP maven 插件进行调配的示例

<plugin>
    <groupId>org.jboss.eap.plugins</groupId>
    <artifactId>eap-maven-plugin</artifactId>
    <version>1.0.0.Final-redhat-00014</version>
    <configuration>
        <channels>
            <channel>
                <manifest>
                    <groupId>org.jboss.eap.channels</groupId>
                    <artifactId>eap-8.0</artifactId>
                </manifest>
            </channel>
        </channels>
        <feature-packs>
            <feature-pack>
                <location>org.keycloak:keycloak-saml-adapter-galleon-pack</location>
            </feature-pack>
        </feature-packs>
        <layers>
            <layer>core-server</layer>
            <layer>web-server</layer>
            <layer>jaxrs-server</layer>
            <layer>datasources-web-server</layer>
            <layer>webservices</layer>
            <layer>keycloak-saml</layer>
            <layer>keycloak-client-saml</layer>
            <layer>keycloak-client-saml-ejb</layer>
        </layers>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>package</goal>
            </goals>
        </execution>
    </executions>
</plugin>

6.2. 配置

SAML 客户端适配器由放置在 WAR 部署中的 XML 文件 /WEB-INF/keycloak-saml.xml 来配置。配置可能类似如下:

<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xsi:schemaLocation="urn:keycloak:saml:adapter https://www.keycloak.org/schema/keycloak_saml_adapter_1_10.xsd">
    <SP entityID="http://localhost:8081/sales-post-sig/"
        sslPolicy="EXTERNAL"
        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
        logoutPage="/logout.jsp"
        forceAuthentication="false"
        isPassive="false"
        turnOffChangeSessionIdOnLogin="false"
        autodetectBearerOnly="false">
        <Keys>
            <Key signing="true" >
                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
                    <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
                    <Certificate alias="http://localhost:8080/sales-post-sig/"/>
                </KeyStore>
            </Key>
        </Keys>
        <PrincipalNameMapping policy="FROM_NAME_ID"/>
        <RoleIdentifiers>
            <Attribute name="Role"/>
        </RoleIdentifiers>
        <RoleMappingsProvider id="properties-based-role-mapper">
            <Property name="properties.resource.location" value="/WEB-INF/role-mappings.properties"/>
        </RoleMappingsProvider>
        <IDP entityID="idp"
             signaturesRequired="true">
        <SingleSignOnService requestBinding="POST"
                             bindingUrl="http://localhost:8081/realms/demo/protocol/saml"
                    />

            <SingleLogoutService
                    requestBinding="POST"
                    responseBinding="POST"
                    postBindingUrl="http://localhost:8081/realms/demo/protocol/saml"
                    redirectBindingUrl="http://localhost:8081/realms/demo/protocol/saml"
                    />
            <Keys>
                <Key signing="true">
                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
                        <Certificate alias="demo"/>
                    </KeyStore>
                </Key>
            </Keys>
        </IDP>
     </SP>
</keycloak-saml-adapter>

您可以使用 ${…​} enclosure 作为系统属性替换。例如 ${jboss.server.config.dir}。要获得 XML 配置文件中不同元素的详细信息,请参阅 Red Hat build of Keycloak SAML Galleon feature pack 详细配置

6.2.1. 保护 WAR

这部分论述了如何通过在 WAR 软件包中添加配置和编辑文件来直接保护 WAR。

创建 keycloak-saml.xml 并在 WAR 的 WEB-INF 目录中创建后,您必须在 web.xml 中将 auth-method 设置为 KEYCLOAK-SAML。您还必须使用标准 servlet 安全性为您的 URL 指定角色基础限制。以下是 web.xml 文件示例:

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">

	<module-name>customer-portal</module-name>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Admins</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Customers</web-resource-name>
            <url-pattern>/customers/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

    <login-config>
        <auth-method>KEYCLOAK-SAML</auth-method>
        <realm-name>this is ignored currently</realm-name>
    </login-config>

    <security-role>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <role-name>user</role-name>
    </security-role>
</web-app>

auth-method 设置外的所有标准 servlet 设置。

您不必打开 WAR,以便使用红帽构建的 Keycloak 来保护它。另外,您还可以通过红帽构建 Keycloak SAML Adapter 子系统进行外部保护。虽然您不必将 KEYCLOAK-SAML 指定为 auth-method,您仍必须在 web.xml 中定义 安全约束。但是,您不必创建 WEB-INF/keycloak-saml.xml 文件。这个元数据改为在服务器的 domain.xmlstandalone.xml 子系统配置部分中的 XML 中定义。

<extensions>
  <extension module="org.keycloak.keycloak-saml-adapter-subsystem"/>
</extensions>

<profile>
  <subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
    <secure-deployment name="WAR MODULE NAME.war">
      <SP entityID="APPLICATION URL">
        ...
      </SP>
    </secure-deployment>
  </subsystem>
</profile>

secure-deployment name 属性标识您要保护的 WAR。其值是 web.xml 中定义的 module-name,并附加 .war。其余的配置使用与 General Adapter Config 中定义的 keycloak-saml.xml 配置相同的 XML 语法。

配置示例:

<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
  <secure-deployment name="saml-post-encryption.war">
    <SP entityID="http://localhost:8080/sales-post-enc/"
        sslPolicy="EXTERNAL"
        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
        logoutPage="/logout.jsp"
        forceAuthentication="false">
      <Keys>
        <Key signing="true" encryption="true">
          <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
            <PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/>
            <Certificate alias="http://localhost:8080/sales-post-enc/"/>
          </KeyStore>
        </Key>
      </Keys>
      <PrincipalNameMapping policy="FROM_NAME_ID"/>
      <RoleIdentifiers>
        <Attribute name="Role"/>
      </RoleIdentifiers>
      <IDP entityID="idp">
        <SingleSignOnService signRequest="true"
            validateResponseSignature="true"
            requestBinding="POST"
            bindingUrl="http://localhost:8080/realms/saml-demo/protocol/saml"/>

        <SingleLogoutService
            validateRequestSignature="true"
            validateResponseSignature="true"
            signRequest="true"
            signResponse="true"
            requestBinding="POST"
            responseBinding="POST"
            postBindingUrl="http://localhost:8080/realms/saml-demo/protocol/saml"
            redirectBindingUrl="http://localhost:8080/realms/saml-demo/protocol/saml"/>
        <Keys>
          <Key signing="true" >
            <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
              <Certificate alias="saml-demo"/>
            </KeyStore>
          </Key>
        </Keys>
      </IDP>
    </SP>
   </secure-deployment>
</subsystem>

6.2.3. 为 JSESSIONID cookie 设置 SameSite 值

浏览器正在计划将 Cookie 的 SameSite 属性的默认值设为 Lax。此设置意味着,只有请求来自同一域时才会将 Cookie 发送到应用程序。这个行为可能会影响 SAML POST 绑定,这可能会无法正常工作。要保留 SAML 适配器的完整功能,我们建议为容器创建的 JSESSIONID cookie 将 SameSite 值设置为 None。不这样做可能会导致在每次请求红帽构建的 Keycloak 时重置容器的会话。

注意

为了避免将 SameSite 属性设置为 None,请考虑切换到 REDIRECT 绑定(如果可以接受)或 OIDC 协议(如果不需要这个临时解决方案)。

要将 Wildfly/EAP 中的 JSESSIONID cookie 的 SameSite 值设置为 None,请将包含以下内容的文件 undertow-handlers.conf 添加到应用的 WEB-INF 目录中:

samesite-cookie(mode=None, cookie-pattern=JSESSIONID)

从版本 19.1.0 开始,对此配置的支持包括在 Wildfly 中。

6.3. 使用身份提供程序注册

对于每个基于 servlet 的适配器,您注册了 assert 消费者服务 URL 的端点和单一注销服务必须是 servlet 应用的基本 URL,并附加了 /saml,即 https://example.com/contextPath/saml

6.4. 退出

您可以从 Web 应用注销的方法有多种。对于 Jakarta EE servlet 容器,您可以调用 HttpServletRequest.logout ()。对于任何其他浏览器应用程序,您可以在具有安全约束的 Web 应用程序的任何 url 中点浏览器,并传递查询参数 GLO,即 http://myapp?GLO=true。如果您有带有浏览器的 SSO 会话,这将退出。

6.4.1. 在集群环境中注销

在内部,SAML 适配器会在 SAML 会话索引、主体名称(已知)和 HTTP 会话 ID 之间存储映射。此映射可以在 JBoss 应用服务器系列中维护(WildFly 10/11,EAP 6/7),以用于可分布式应用。作为 precondition,HTTP 会话需要在集群中分发(例如,应用程序在应用程序的 web.xml中被标记为 &lt ;distributable /> 标签)。

要启用功能,请在 /WEB_INF/web.xml 文件中添加以下部分:

<context-param>
    <param-name>keycloak.sessionIdMapperUpdater.classes</param-name>
    <param-value>org.keycloak.adapters.saml.wildfly.infinispan.InfinispanSessionCacheIdMapperUpdater</param-value>
</context-param>

如果部署的会话缓存被命名为 deployment-cache,用于 SAML 映射的缓存将命名为 deployment-cache.ssoCache。缓存的名称可以被上下文参数 keycloak.sessionIdMapperUpdater.infinispan.cacheName 覆盖。包含缓存的缓存容器将与包含部署会话缓存的缓存容器相同,但可以被一个上下文参数 keycloak.sessionIdMapperUpdater.infinispan.containerName 覆盖。

默认情况下,SAML 映射缓存的配置将派生自会话缓存。可以在服务器的缓存配置部分中手动覆盖配置,与其他缓存相同。

目前,为了提供可靠的服务,建议将复制缓存用于 SAML 会话缓存。使用分布式缓存可能会导致 SAML 注销请求进入没有访问 SAML 会话索引的节点到 HTTP 会话映射,这会导致注销失败。

6.4.2. 在多集群部署中退出

处理跨越多个数据中心的会话需要特殊处理。想到以下场景:

  1. 登录请求在数据中心 1 中的集群内处理。
  2. 管理员发出特定 SAML 会话的注销请求,即数据中心 2 中的请求登录。

数据中心 2 必须注销数据中心 1 中(以及共享 HTTP 会话的所有其他数据中心)存在的所有会话。

要涵盖这种情况,上述 SAML 会话缓存不仅需要在单个集群中复制,例如通过独立的 Infinispan/JDG 服务器复制所有数据中心:

  1. 缓存必须添加到独立 Infinispan/JDG 服务器。
  2. 上一项中的缓存必须添加为相应 SAML 会话缓存的远程存储。

在部署期间发现 SAML 会话缓存上存在远程存储后,将监视是否有更改,并将相应地更新本地 SAML 会话缓存。

6.5. 获取断言属性

成功 SAML 登录后,您的应用程序代码可能希望获取通过 SAML 断言传递的属性值。HttpServletRequest.getUserPrincipal () 返回 Principal 对象,您可以键入cast 到名为 org.keycloak.adapters.saml.SamlPrincipal 的红帽构建的 Keycloak 特定的类。此对象允许您查看原始断言,还可方便地查找属性值。

package org.keycloak.adapters.saml;

public class SamlPrincipal implements Serializable, Principal {
    /**
     * Get full saml assertion
     *
     * @return
     */
    public AssertionType getAssertion() {
       ...
    }

    /**
     * Get SAML subject sent in assertion
     *
     * @return
     */
    public String getSamlSubject() {
        ...
    }

    /**
     * Subject nameID format
     *
     * @return
     */
    public String getNameIDFormat() {
        ...
    }

    @Override
    public String getName() {
        ...
    }

    /**
     * Convenience function that gets Attribute value by attribute name
     *
     * @param name
     * @return
     */
    public List<String> getAttributes(String name) {
        ...

    }

    /**
     * Convenience function that gets Attribute value by attribute friendly name
     *
     * @param friendlyName
     * @return
     */
    public List<String> getFriendlyAttributes(String friendlyName) {
        ...
    }

    /**
     * Convenience function that gets first  value of an attribute by attribute name
     *
     * @param name
     * @return
     */
    public String getAttribute(String name) {
        ...
    }

    /**
     * Convenience function that gets first  value of an attribute by attribute name
     *
     *
     * @param friendlyName
     * @return
     */
    public String getFriendlyAttribute(String friendlyName) {
        ...
    }

    /**
     * Get set of all assertion attribute names
     *
     * @return
     */
    public Set<String> getAttributeNames() {
        ...
    }

    /**
     * Get set of all assertion friendly attribute names
     *
     * @return
     */
    public Set<String> getFriendlyNames() {
        ...
    }
}

6.6. 错误处理

红帽构建的 Keycloak 有一些错误处理功能,用于基于 servlet 的客户端适配器。在身份验证中遇到错误时,客户端适配器将调用 HttpServletResponse.sendError ()。您可以在 web.xml 文件中设置一个 error-page,以处理这个错误。客户端适配器可以抛出 400、401、403 和 500 错误。

<error-page>
    <error-code>403</error-code>
    <location>/ErrorHandler</location>
</error-page>

客户端适配器还设置您可以检索的 HttpServletRequest 属性。属性名称为 org.keycloak.adapters.spi.AuthenticationError。Typecast this object to: org.keycloak.adapters.saml.SamlAuthenticationError.此类可以告诉您的确切发生情况。如果没有设置此属性,则适配器不会负责错误代码。

public class SamlAuthenticationError implements AuthenticationError {
    public static enum Reason {
        EXTRACTION_FAILURE,
        INVALID_SIGNATURE,
        ERROR_STATUS
    }

    public Reason getReason() {
        return reason;
    }
    public StatusResponseType getStatus() {
        return status;
    }
}

6.7. 故障排除

排除问题的最佳方法是在客户端适配器和红帽构建的 Keycloak 服务器中为 SAML 启用调试。使用您的日志记录框架,将 org.keycloak.saml 软件包的日志级别设置为 DEBUG。打开此打开后,您可以看到要发送到服务器的 SAML 请求和响应文档。

6.8. 多租户

SAML 提供 Multi Tenancy,这意味着单个目标应用程序(WAR)可以使用多个红帽构建的 Keycloak 域进行保护。域可以位于同一红帽构建的 Keycloak 实例或不同实例上。

要做到这一点,应用程序必须具有多个 keycloak-saml.xml 适配器配置文件。

虽然您可以有多个 WAR 实例,并将不同的适配器配置文件部署到不同的上下文路径,但这可能不太方便,您可能还希望根据上下文路径以外的其他一些域来选择域。

红帽构建的 Keycloak 使可以具有自定义配置解析器,因此您可以选择为每个请求使用哪个适配器配置。在 SAML 中,配置只在登录处理中有意义;当用户登录后,会话会被身份验证,如果 keycloak-saml.xml 返回的不同,则会话无关紧要。因此,为同一会话返回相同的配置是正确的方法。

要达到此目的,请创建 org.keycloak.adapters.saml.SamlConfigResolver 的实现。以下示例使用 Host 标头来查找正确的配置,并从应用程序的 Java 类路径加载它以及相关的元素:

package example;

import java.io.InputStream;
import org.keycloak.adapters.saml.SamlConfigResolver;
import org.keycloak.adapters.saml.SamlDeployment;
import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.saml.common.exceptions.ParsingException;

public class SamlMultiTenantResolver implements SamlConfigResolver {

    @Override
    public SamlDeployment resolve(HttpFacade.Request request) {
        String host = request.getHeader("Host");
        String realm = null;
        if (host.contains("tenant1")) {
            realm = "tenant1";
        } else if (host.contains("tenant2")) {
            realm = "tenant2";
        } else {
            throw new IllegalStateException("Not able to guess the keycloak-saml.xml to load");
        }

        InputStream is = getClass().getResourceAsStream("/" + realm + "-keycloak-saml.xml");
        if (is == null) {
            throw new IllegalStateException("Not able to find the file /" + realm + "-keycloak-saml.xml");
        }

        ResourceLoader loader = new ResourceLoader() {
            @Override
            public InputStream getResourceAsStream(String path) {
                return getClass().getResourceAsStream(path);
            }
        };

        try {
            return new DeploymentBuilder().build(is, loader);
        } catch (ParsingException e) {
            throw new IllegalStateException("Cannot load SAML deployment", e);
        }
    }
}

您还必须配置与 web.xml 中的 keycloak.config.resolver context-param 搭配使用的 SamlConfigResolver 实现:

<web-app>
    ...
    <context-param>
        <param-name>keycloak.config.resolver</param-name>
        <param-value>example.SamlMultiTenantResolver</param-value>
    </context-param>
</web-app>

6.9. 红帽构建的 Keycloak 特定错误

红帽构建的 Keycloak 服务器可以在 SAML 响应中向客户端应用程序发送错误,该响应可能包含 SAML 状态,例如:

<samlp:Status>
  <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder">
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"/>
  </samlp:StatusCode>
  <samlp:StatusMessage>authentication_expired</samlp:StatusMessage>
</samlp:Status>

当用户进行身份验证并具有 SSO 会话时,Red Hat build of Keycloak 会发送这个错误,但在当前浏览器标签页中过期身份验证,因此红帽构建的 Keycloak 服务器无法自动进行 SSO 重新身份验证,并以成功响应重定向到客户端。当客户端应用程序收到这种类型的错误时,最好立即重试身份验证,并向红帽构建的 Keycloak 服务器发送新的 SAML 请求,这通常应该始终因为 SSO 会话和重定向而验证用户。如果服务器返回注释状态,SAML 适配器执行会自动重试。服务器管理指南 中的更多详细信息。

查看 'keycloak-saml.xml' 配置文件的详细元素列表。

本章包含红帽构建 Keycloak SAML Galleon 功能软件包使用的 keycloak-saml.xml 配置文件的详细元素列表。

7.1. SP 元素

以下是 SP 元素属性的说明:

<SP entityID="sp"
    sslPolicy="ssl"
    nameIDPolicyFormat="format"
    forceAuthentication="true"
    isPassive="false"
    keepDOMAssertion="true"
    autodetectBearerOnly="false">
...
</SP>
entityID
这是此客户端的标识符。IdP 需要这个值来确定客户端是与之通信的。此设置是 REQUIRED
sslPolicy
这是适配器将强制执行的 SSL 策略。有效值为: ALLEXTERNALNONE。对于 ALL,所有请求都必须通过 HTTPS。对于 EXTERNAL,只有非专用 IP 地址必须通过 HTTPS 在线进行。对于 NONE,不需要通过 HTTPS 进行请求。此设置是 OPTIONAL。默认值为 EXTERNAL
nameIDPolicyFormat
SAML 客户端可以请求特定的 NameID Subject 格式。如果您希望特定格式,请填写这个值。它必须是标准的 SAML 格式标识符: urn:oasis:names:tc:2.0:nameid-format:transient。此设置是 OPTIONAL。默认情况下,不请求特殊格式。
forceAuthentication
SAML 客户端可以请求用户被重新验证,即使它们已在 IdP 中登录。把它设置为 true 来启用。此设置是 OPTIONAL。默认值为 false
isPassive
SAML 客户端可以请求用户永远不会被要求进行身份验证,即使它们没有在 IdP 中登录。把它设置为 true。不要与 forceAuthentication 一起使用,因为它们是相反的。此设置是 OPTIONAL。默认值为 false
turnOffChangeSessionIdOnLogin
会话 ID 默认在某些平台上成功登录时更改,以插入安全攻击向量。把它更改为 true 以禁用此功能。建议您不要将其关闭。默认值为 false
autodetectBearerOnly
如果您的应用程序同时提供 Web 应用程序和 Web 服务(如 SOAP 或 REST),则这应设为 true。它允许您将 Web 应用程序的未经身份验证的用户重定向到红帽构建的 Keycloak 登录页面,但会向未经身份验证的 SOAP 或 REST 客户端发送 HTTP 401 状态代码,因为他们不知道到登录页面。红帽构建的 Keycloak 会自动探测 SOAP 或 REST 客户端,具体取决于典型的标头,如 X-Requested-With、PSOAPActionAccept。默认值为 false
logoutPage
这会将页面设置为在注销后显示。如果页面是一个完整的 URL,如 http://web.example.com/logout.html,用户会在退出后使用 HTTP 302 状态代码重定向到该页面。如果指定了没有方案部分的链接,如 /logout.jsp,则页面会在注销后显示,无论它是否位于 web.xml 中的 security-constraint 声明中,并且页面会相对于部署上下文 root 解析。
keepDOMAssertion
此属性应设置为 true,以便适配器将断言的 DOM 表示以其原始形式存储在与请求关联的 SamlPrincipal 内。可以使用主体中的方法 getAssertionDocument 来检索断言文档。这在重新显示已签名的断言时特别有用。返回的文档是生成解析红帽构建的 Keycloak 服务器收到的 SAML 响应生成的文档。此设置是 OPTIONAL,其默认值为 false (文档没有保存在主体中)。

7.2. 服务供应商密钥和关键元素

如果 IdP 要求客户端应用程序(或 SP)为其所有请求签名,如果 IdP 将加密断言,则必须定义用于执行此操作的密钥。对于客户端签名的文档,您必须同时定义用于签署文档的私钥和公钥或证书。对于加密,您只需要定义用于解密它的私钥。

可以通过两种方式描述您的密钥。它们可以存储在 Java KeyStore 中,或者您可以直接以 PEM 格式在 keycloak-saml.xml 中复制/粘贴密钥。

        <Keys>
            <Key signing="true" >
               ...
            </Key>
        </Keys>

Key 元素有两个可选属性 signingencryption。当设置为 true 时,告诉适配器使用哪个密钥。如果这两个属性都设为 true,则密钥将用于签名文档和解密加密的断言。您必须至少将其中一个属性设置为 true。

7.2.1. keystore 元素

Key 元素中,您可以从 Java Keystore 加载密钥和证书。这在 KeyStore 元素中声明。

        <Keys>
            <Key signing="true" >
                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
                    <PrivateKey alias="myPrivate" password="test123"/>
                    <Certificate alias="myCertAlias"/>
                </KeyStore>
            </Key>
        </Keys>

以下是使用 KeyStore 元素定义的 XML 配置属性。

file
密钥存储的文件路径。此选项是 OPTIONAL。必须设置 file 或 resource 属性。
resource
KeyStore 的 WAR 资源路径。这是对 ServletContext.getResourceAsStream ()方法调用中的一个路径。此选项是 OPTIONAL。必须设置 file 或 resource 属性。
password
KeyStore 的密码。此选项是 REQUIRED

如果您要定义 SP 将用来签署文档的密钥,还必须在 Java KeyStore 中指定对私钥和证书的引用。上例中的 PrivateKeyCertificate 元素定义了一个 alias,指向密钥存储内密钥或证书。密钥存储需要额外的密码来访问私钥。在 PrivateKey 元素中,您必须在 password 属性中定义此密码。

7.2.2. 密钥 PEMS

Key 元素中,您直接使用子元素 PrivateKeyPemPublicKeyPemCertificatePem,直接声明您的密钥和证书。这些元素中包含的值必须符合 PEM 密钥格式。如果您使用 openssl 或类似命令行工具生成密钥,通常使用这个选项。

<Keys>
   <Key signing="true">
      <PrivateKeyPem>
         2341251234AB31234==231BB998311222423522334
      </PrivateKeyPem>
      <CertificatePem>
         211111341251234AB31234==231BB998311222423522334
      </CertificatePem>
   </Key>
</Keys>

7.3. SP PrincipalNameMapping 元素

这个元素是可选的。在创建从 HttpServletRequest.getUserPrincipal () 等方法获取的 Java Principal 对象时,您可以定义 Principal.getName () 方法返回的名称。

<SP ...>
  <PrincipalNameMapping policy="FROM_NAME_ID"/>
</SP>

<SP ...>
  <PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="email" />
</SP>

policy 属性定义用于填充此值的策略。此属性的可能值有:

FROM_NAME_ID
这个策略只使用任何 SAML 主题值。这是默认设置
FROM_ATTRIBUTE
这将从服务器接收的 SAML 断言中声明的属性之一中拉取该值。您将需要指定要在 attribute XML 属性中使用的 SAML 断言属性的名称。

7.4. RoleIdentifiers element

RoleIdentifiers 元素定义从用户接收的断言中的 SAML 属性应用作用户的 Jakarta EE 安全上下文的角色标识符。

<RoleIdentifiers>
     <Attribute name="Role"/>
     <Attribute name="member"/>
     <Attribute name="memberOf"/>
</RoleIdentifiers>

默认情况下 role 属性值转换为 Jakarta EE 角色。有些 IdP 使用 membermemberOf 属性断言发送角色。您可以定义一个或多个 Attribute 元素,以指定哪些 SAML 属性必须转换为角色。

7.5. RoleMappingsProvider 元素

RoleMappingsProvider 是一个可选元素,它允许指定 org.keycloak.adapters.saml.RoleMappingsProvider SPI 实现的 id 规格,供 SAML 适配器使用。

当红帽构建的 Keycloak 用作 IDP 时,可以在将角色添加到 SAML 断言前,使用内置角色映射程序来映射任何角色。但是,SAML 适配器可用于向第三方 IDP 发送 SAML 请求,在这种情况下,可能需要将从断言中提取的角色映射到 SP 所需的一组不同的角色。RoleMappingsProvider SPI 允许配置可用于执行必要映射的可插拔角色映射。

供应商的配置如下:

...
<RoleIdentifiers>
    ...
</RoleIdentifiers>
<RoleMappingsProvider id="properties-based-role-mapper">
    <Property name="properties.resource.location" value="/WEB-INF/role-mappings.properties"/>
</RoleMappingsProvider>
<IDP>
    ...
</IDP>

id 属性标识要使用哪个已安装提供程序。Property 子元素可以多次使用来指定提供程序的配置属性。

7.5.1. 基于属性的角色映射提供程序

红帽 Keycloak 的构建包括 RoleMappingsProvider 实现,它使用 属性文件 执行角色映射。此提供程序由 基于 id 属性的-role-mapper 标识,由 org.keycloak.adapters.saml.PropertiesBasedRoleMapper 类实现。

此提供程序依赖于两个配置属性,它们可以用来指定要使用的 属性 文件的位置。首先,它会检查 properties.file.location 是否被设置,使用配置的值来查找文件系统中的 properties 文件。如果配置的文件不位于,则提供程序会抛出 RuntimeException。以下片段演示了使用 properties.file.configuration 选项从文件系统中 /opt/mappers/ 目录中加载 roles.properties 文件的 provider 示例:

    <RoleMappingsProvider id="properties-based-role-mapper">
        <Property name="properties.file.location" value="/opt/mappers/roles.properties"/>
    </RoleMappingsProvider>

如果没有设置 properties.file.location,则供应商会检查 properties.resource.location 属性,使用配置的值来从 WAR 资源中加载 properties 文件。如果这个配置属性也不存在,则供应商默认尝试从 /WEB-INF/role-mappings.properties 中加载文件。无法从资源加载文件将导致提供程序抛出 RuntimeException。以下片段演示了使用 properties.resource.location 从应用程序的 /WEB-INF/conf/ 目录中加载 roles.properties 文件的供应商示例:

    <RoleMappingsProvider id="properties-based-role-mapper">
        <Property name="properties.resource.location" value="/WEB-INF/conf/roles.properties"/>
    </RoleMappingsProvider>

属性文件 可以包含角色和主体作为键,以及以逗号分隔为值的零个或多个角色的列表。调用时,实施将迭代从断言提取和检查各个角色的一组角色(如果存在映射)。如果角色映射到空角色,它将被丢弃。如果它映射到一个或多个不同角色的集合,则会在结果集中设置这些角色。如果没有为角色找到映射,则会包含在结果集中。

处理角色后,实施将检查从断言中提取的主体是否包含条目 属性文件。如果存在主体的映射,则所有作为值列出的角色都会添加到结果集中。这允许将额外的角色分配给主体。

例如,假设供应商已使用以下属性文件配置:

roleA=roleX,roleY
roleB=

kc_user=roleZ

如果从断言中提取了主体 kc_user,使用 roleAroleBroleC,则分配给主体的最终角色集合为 roleCroleXroleYroleZ,因为 roleA 被映射到 roleXroleYroleB 被映射到空角色 - 因此被丢弃,因此 roleC 将用作 roleC,最后添加到 kc_user _user 主体。

注: 要在角色名称中使用空格进行映射,请对空间使用 unicode 替换。例如,传入的 'role A' 显示为:

role\u0020A=roleX,roleY

7.6. IDP 元素

IDP 元素中的所有描述 SP 与之通信的身份提供程序(身份验证服务器)的设置。

<IDP entityID="idp"
     signaturesRequired="true"
     signatureAlgorithm="RSA_SHA1"
     signatureCanonicalizationMethod="http://www.w3.org/2001/10/xml-exc-c14n#">
...
</IDP>

以下是您可以在 IDP 元素声明中指定的属性配置选项。

entityID
这是 IDP 的签发者 ID。此设置是 REQUIRED
signaturesRequired
如果设置为 true,则客户端适配器都将为其发送到 IDP 的每个文档进行签名。另外,客户端预期 IDP 将签署发送到它的任何文档。此交换机为所有请求和响应类型设置默认值,但稍后您会看到您可以对其有一些精细的控制。此设置是 OPTIONAL,默认为 false
signatureAlgorithm
这是 IDP 期望签名的文档使用的签名算法。允许的值有: RSA_SHA1RSA_SHA256RSA_SHA512DSA_SHA1。此设置是 OPTIONAL,默认为 RSA_SHA256。请注意,基于 SHA1 的算法已弃用,并可以在以后删除。我们建议使用一些更安全的算法,而不是 *_SHA1。另外,对于 *_SHA1 算法,如果 SAML 服务器(通常是红帽构建的 Keycloak)在 Java 17 或更高版本上运行,验证签名无法正常工作。
signatureCanonicalizationMethod
这是 IDP 期望签名的文档使用的签名规范方法。此设置是 OPTIONAL。默认值为 http://www.w3.org/2001/10/xml-exc-c14n#,大多数 IDP 都应该使用。
metadataUrl
用于检索 IDP 元数据的 URL 目前,这仅用于定期获取签名和加密密钥,允许在 IDP 上循环这些密钥,而无需在 SP 端进行手动更改。

7.7. IDP AllowedClockSkew 子元素

AllowedClockSkew 可选子元素定义 IDP 和 SP 之间的允许的时钟偏移。默认值为 0。

<AllowedClockSkew unit="MILLISECONDS">3500</AllowedClockSkew>
unit
可以定义附加到这个元素的值的时间单元。允许的值是 MICROSECONDS、MILLISECONDS、MINUTES、NANOSECONDS 和 SECONDS。这是 OPTIONAL。默认值为 SECONDS

7.8. IDP SingleSignOnService 子元素

SingleSignOnService 子元素定义 IDP 的登录 SAML 端点。当客户端适配器希望登录时,会通过此元素中的设置将请求发送到 IDP 格式。

<SingleSignOnService signRequest="true"
                     validateResponseSignature="true"
                     requestBinding="post"
                     bindingUrl="url"/>

以下是您可以在此元素上定义的配置属性:

signRequest
客户端是否应该为 authn 请求签名?此设置是 OPTIONAL。默认为 IDP signaturesRequired 元素值的任何值。
validateResponseSignature
客户端是否应该预期 IDP 为从 authn 请求发送的断言响应文档签名?此设置是 OPTIONAL。默认为 IDP signaturesRequired 元素值的任何值。
requestBinding
这是用于与 IDP 通信的 SAML 绑定类型。此设置是 OPTIONAL。默认值为 POST,但您也可以将其设置为 REDIRECT
responseBinding
SAML 允许客户端请求它希望使用 authn 响应的绑定类型。这个值可以是 POSTREDIRECT。此设置是 OPTIONAL。默认值是客户端不会为响应请求特定的绑定类型。
assertionConsumerServiceUrl
IDP 登录服务应向其发送响应的断言消费者服务的 URL。此设置是 OPTIONAL。默认情况下,它没有设置,依赖于 IdP 中的配置。设置后,它必须以 /saml 结尾,例如 http://sp.domain.com/my/endpoint/for/saml。此属性的值在 SAML AuthnRequest 消息的 AssertionConsumerServiceURL 属性中发送。此属性通常由 responseBinding 属性提供。
bindingUrl
这是客户端要发送请求的 IDP 登录服务的 URL。此设置是 REQUIRED

7.9. IDP SingleLogoutService 子元素

SingleLogoutService 子元素定义 IDP 的 logout SAML 端点。当客户端适配器希望登出时,客户端适配器会通过此元素中的设置将请求发送到 IDP 格式的 IDP 格式。

<SingleLogoutService validateRequestSignature="true"
                     validateResponseSignature="true"
                     signRequest="true"
                     signResponse="true"
                     requestBinding="redirect"
                     responseBinding="post"
                     postBindingUrl="posturl"
                     redirectBindingUrl="redirecturl">
signRequest
客户端签署注销请求,它是否向 IDP 发出?此设置是 OPTIONAL。默认为 IDP signaturesRequired 元素值的任何值。
signResponse
客户端签署注销响应器是否发送到 IDP 请求?此设置是 OPTIONAL。默认为 IDP signaturesRequired 元素值的任何值。
validateRequestSignature
客户端是否应该预期 IDP 中签名的注销请求文档?此设置是 OPTIONAL。默认为 IDP signaturesRequired 元素值的任何值。
validateResponseSignature
客户端是否应该预期来自 IDP 签名的注销响应文档?此设置是 OPTIONAL。默认为 IDP signaturesRequired 元素值的任何值。
requestBinding
这是用于将 SAML 请求发送到 IDP 的 SAML 绑定类型。此设置是 OPTIONAL。默认值为 POST,但您也可以将其设置为 REDIRECT。
responseBinding
这是用于向 IDP 通信 SAML 响应的 SAML 绑定类型。这个值可以是 POSTREDIRECT。此设置是 OPTIONAL。默认值为 POST,但您也可以将其设置为 REDIRECT
postBindingUrl
这是使用 POST 绑定时 IDP 的 logout 服务的 URL。如果使用 POST 绑定,则此设置为 REQUIRED
redirectBindingUrl
这是使用 REDIRECT 绑定时 IDP 注销服务的 URL。如果使用 REDIRECT 绑定,则此设置为 REQUIRED

7.10. IDP Keys 子元素

IDP 的 Keys 子元素仅用于定义用来验证 IDP 签署的文档的证书或公钥。它的定义方式与 SP 的 Keys 元素 相同。但是,您只需要定义一个证书或公钥引用。请注意,如果 IDP 和 SP 分别由红帽构建 Keycloak 服务器和适配器实现,则不需要为签名验证指定密钥,如下所示。

可以将 SP 配置为从公布的证书获取 IDP 签名验证的公钥,只要 SP 和 IDP 由红帽构建的 Keycloak 实施。这可以通过删除 Keys 子元素中的所有签名验证密钥声明来实现。如果 Keys 子元素将保留为空,则可以完全省略它。然后,密钥由 SP 从 SAML 描述符自动获取,该位置是从 IDP SingleSignOnService 子元素 中指定的 SAML 端点 URL 中获取的位置。用于 SAML 描述符检索的 HTTP 客户端的设置通常需要其他配置,但可以在 IDP HttpClient 子元素 中配置。

还可以指定多个密钥来签名验证。这可以通过在 Keys 子元素中声明多个 Key 元素,该元素将 signing 属性设置为 true 来完成。这在轮转 IDP 签名密钥时很有用:当新的 SAML 协议消息和断言使用新密钥签名时,通常会有一个过渡周期,但之前密钥签名的密钥仍被接受。

无法将红帽构建的 Keycloak 配置为自动获取签名验证的密钥,并定义额外的静态签名验证密钥。

       <IDP entityID="idp">
            ...
            <Keys>
                <Key signing="true">
                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
                        <Certificate alias="demo"/>
                    </KeyStore>
                </Key>
            </Keys>
        </IDP>

7.11. IDP HttpClient 子元素

HttpClient 可选子元素定义 HTTP 客户端的属性,用于在启用 时通过 SAML 描述符自动获取包含 IDP 签名验证的公钥的证书。???

<HttpClient connectionPoolSize="10"
            disableTrustManager="false"
            allowAnyHostname="false"
            clientKeystore="classpath:keystore.jks"
            clientKeystorePassword="pwd"
            truststore="classpath:truststore.jks"
            truststorePassword="pwd"
            proxyUrl="http://proxy/"
            socketTimeout="5000"
            connectionTimeout="6000"
            connectionTtl="500" />
connectionPoolSize
此配置选项定义与红帽构建的 Keycloak 服务器的连接应池数量。这是 OPTIONAL。默认值为 10
disableTrustManager
如果红帽构建的 Keycloak 服务器需要 HTTPS,且此配置选项被设置为 true,则不必指定信任存储。此设置应只在开发过程中使用,且永远不会在生产环境中使用,因为它将禁用 SSL 证书的验证。这是 OPTIONAL。默认值为 false
allowAnyHostname
如果红帽构建的 Keycloak 服务器需要 HTTPS,并且此配置选项被设置为 true,红帽构建的 Keycloak 服务器的证书会通过信任存储进行验证,但不会完成主机名验证。此设置应只在开发过程中使用,且不要在生产环境中使用,因为它部分将禁用 SSL 证书的验证。此设置在测试环境中很有用。这是 OPTIONAL。默认值为 false
truststore
该值是信任存储文件的文件路径。如果您使用 classpath: 前缀,则会从部署的 classpath 获取 truststore。用于向红帽构建的 Keycloak 服务器的传出 HTTPS 通信。发出 HTTPS 请求的客户端需要一种方法来验证它们要与之通信的服务器的主机。这是信任存储的作用。密钥存储包含一个或多个可信主机证书或证书颁发机构。您可以通过提取红帽构建的 Keycloak 服务器 SSL 密钥存储的公共证书来创建此信任存储。这是 REQUIRED,除非 disableTrustManagertrue
truststorePassword
truststore 的密码。如果设置了 truststore,并且信任存储需要密码,则这是 REQUIRED
clientKeystore
这是密钥存储文件的文件路径。当适配器向红帽构建的 Keycloak 服务器发出 HTTPS 请求时,此密钥存储包含双向 SSL 的客户端证书。这是 OPTIONAL
clientKeystorePassword
客户端密钥存储和客户端密钥的密码。如果设置了 clientKeystore,则此为 REQUIRED
proxyUrl
用于 HTTP 连接的 HTTP 代理的 URL。这是 OPTIONAL
socketTimeout
在建立连接后等待数据的超时(以毫秒为单位)。两个数据数据包之间的最大不活跃时间。超时值为零解释为无限超时。负值被解释为 undefined (如果适用系统默认值)。默认值为 -1。这是 OPTIONAL
connectionTimeout
以毫秒为单位与远程主机建立连接的超时。超时值为零解释为无限超时。负值被解释为 undefined (如果适用系统默认值)。默认值为 -1。这是 OPTIONAL
connectionTtl
以毫秒为单位对客户端连接生存时间。小于或等于零的值被解释为无限值。默认值为 -1。这是 OPTIONAL

第 8 章 配置 mod_auth_mellon Apache 模块

使用红帽构建的 Keycloak 配置 mod_auth_mellon Apache 模块。

mod_auth_mellon 是 Apache 的身份验证模块。如果您的语言/环境支持使用 Apache HTTPD 作为代理,您可以使用 mod_auth_mellon 使用 SAML 保护 Web 应用。有关此模块的更多详细信息,请参阅 mod_auth_mellon GitHub 仓库。

警告

Red Hat build of Keycloak 不提供对 mod_auth_mellon 的任何官方支持。以下说明是最佳的,可能不是最新的。本章假定服务器是 RHEL 系统。尽管其他 Linux 系统需要相似的步骤。为了获得更多详细信息,我们建议您使用官方的 mod_auth_mellon 文档。

要配置 mod_auth_mellon,您需要以下文件:

  • Identity Provider (IdP)实体描述符 XML 文件,该文件描述了与红帽构建的 Keycloak 或其他 SAML IdP 的连接
  • SP 实体描述符 XML 文件,该文件描述了您要保护的应用程序的 SAML 连接和配置。
  • 私钥 PEM 文件,这是 PEM 格式的文本文件,用于定义应用用于签署文档的私钥。
  • 证书 PEM 文件,它是为应用程序定义证书的文本文件。
  • mod_auth_mellon 特定的 Apache HTTPD 模块配置.

如果您已经在 Red Hat build of Keycloak Application server 的域中定义了并注册了客户端应用程序,则红帽构建的 Keycloak 可以生成除 Apache HTTPD 模块配置外所需的所有文件。

执行以下步骤来生成 Apache HTTPD 模块配置。

流程

  1. 进入 SAML 客户端的 Installation 页面。
  2. 选择 Mod Auth Mellon 文件选项。

    图 8.1. mod_auth_mellon config download

    下载 auth-melon 配置
  3. Download 下载包含 XML 描述符和 PEM 文件的 ZIP 文件。

8.1. 使用红帽构建的 Keycloak 配置 mod_auth_mellon

涉及两个主机:

  • 运行红帽构建的 Keycloak 的主机,它将被称为 $idp_host,因为红帽构建的 Keycloak 是一个 SAML 身份提供程序(IdP)。
  • 运行 Web 应用的主机,该主机将称为 $sp_host。在使用 IdP 的 SAML 应用程序中称为服务提供商(SP)。

以下所有步骤都需要以 root 特权在 $sp_host 上执行。

8.1.1. 安装软件包

要安装所需的软件包,您需要:

  • Apache Web Server (httpd)
  • Mellon SAML SP 附加组件模块
  • 创建 X509 证书的工具

要安装所需的软件包,请运行以下命令:

yum install httpd mod_auth_mellon mod_ssl openssl

8.1.2. 为 Apache SAML 创建配置目录

建议您将与 Apache 相关的配置文件保存在一个位置上。

在 Apache 配置根目录 /etc/httpd 下创建名为 saml2 的新目录:

mkdir /etc/httpd/saml2

8.1.3. 配置 Mellon 服务提供商

Apache 附加组件模块的配置文件位于 /etc/httpd/conf.d 目录中,文件名为 .conf。您需要创建 /etc/httpd/conf.d/mellon.conf 文件,并将 Mellon 的配置指令放在该文件中。

Mellon 的配置指令可大致分为两类信息:

  • 使用 SAML 身份验证保护的 URL
  • 引用受保护的 URL 时将使用哪些 SAML 参数。

Apache 配置指令通常遵循 URL 空间中的分级树结构,这些结构称为位置。您需要为 Mellon 指定一个或多个 URL 位置来保护。您具有如何添加应用到每个位置的配置参数的灵活性。您可以将所有必要的参数添加到 location 块中,也可以将 Mellon 参数添加到特定保护位置继承的 URL 位置层次结构中(或两者的某些组合)。由于 SP 常以同样的方式运行,无论哪个位置都触发 SAML 操作,因此此处使用的示例配置将常见 Mellon 配置指令放置在层次结构中的 root 中,然后使用 Mellon 保护的特定位置。此策略可避免为每个受保护的位置复制相同的参数。

这个示例只有一个受保护的位置:https://$sp_host/private。

要配置 Mellon 服务供应商,请执行以下步骤。

流程

  1. 使用以下内容创建文件 /etc/httpd/conf.d/mellon.conf
 <Location / >
    MellonEnable info
    MellonEndpointPath /mellon/
    MellonSPMetadataFile /etc/httpd/saml2/mellon_metadata.xml
    MellonSPPrivateKeyFile /etc/httpd/saml2/mellon.key
    MellonSPCertFile /etc/httpd/saml2/mellon.crt
    MellonIdPMetadataFile /etc/httpd/saml2/idp_metadata.xml
 </Location>
 <Location /private >
    AuthType Mellon
    MellonEnable auth
    Require valid-user
 </Location>
注意

后续步骤中会创建上述代码中引用的一些文件。

8.1.5. 创建服务提供商元数据

在 SAML IdP 和 SP 中,用于交换 SAML 元数据,它们是 XML 格式。元数据的 schema 是标准的,因此相关的 SAML 实体可以互相使用其元数据。您需要:

  • SP 使用的 IdP 的元数据
  • 描述 IdP 提供的 SP 的元数据

SAML 元数据的一个组件是 X509 证书。这些证书用于两个目的:

  • 为 SAML 信息签名,以便接收端可以证明来自预期方的消息。
  • 在传输过程中加密消息(很少使用,因为 SAML 消息通常在 TLS 保护的传输中发生)

如果您已拥有证书颁发机构(CA),也可以使用自己的证书,也可以生成自签名证书。在本示例中简单性,使用了自签名证书。

因为 Mellon 的 SP 元数据必须反映已安装 mod_auth_mellon 版本的功能,所以必须是有效的 SP 元数据 XML,且必须包含 X509 证书(创建错误时,除非您熟悉 X509 证书生成)最先生成 SP 元数据的方式是使用 mod_auth_mellon 软件包中包含的工具(mellon_create_metadata.sh)。之后,生成的元数据始终可以被编辑,因为它是一个文本文件。该工具还创建您的 X509 密钥和证书。

SAML IdP 和 SPs 使用称为 EntityID 的唯一名称来识别自己。要使用 Mellon 元数据创建工具,您需要:

  • EntityID,通常是 SP 的 URL,通常是可以检索 SP 元数据的 SP 的 URL
  • 将消耗 SP 的 SAML 消息的 URL,Mellon 调用 MellonEndPointPath。

要创建 SP 元数据,请执行以下步骤。

流程

  1. 创建几个帮助程序 shell 变量:

    fqdn=`hostname`
    mellon_endpoint_url="https://${fqdn}/mellon"
    mellon_entity_id="${mellon_endpoint_url}/metadata"
    file_prefix="$(echo "$mellon_entity_id" | sed 's/[^A-Za-z.]/_/g' | sed 's/__*/_/g')"
  2. 运行以下命令调用 Mellon 元数据创建工具:

    /usr/libexec/mod_auth_mellon/mellon_create_metadata.sh $mellon_entity_id $mellon_endpoint_url
  3. 将生成的文件移动到其目的地(在上面创建的 /etc/httpd/conf.d/mellon.conf 文件中引用):

    mv ${file_prefix}.cert /etc/httpd/saml2/mellon.crt
    mv ${file_prefix}.key /etc/httpd/saml2/mellon.key
    mv ${file_prefix}.xml /etc/httpd/saml2/mellon_metadata.xml

假设:红帽构建的 Keycloak IdP 已在 $idp_host 上安装。

Red Hat build of Keycloak 支持多个租期,其中所有用户、客户端等都分组到一个域里。每个域都独立于其他域。您可以在红帽构建的 Keycloak 中使用现有域,但本示例演示了如何创建名为 test_realm 的新域,并使用该域。

所有这些操作都使用红帽构建的 Keycloak 管理控制台来执行。您必须具有 $idp_host 的 admin 用户名和密码才能执行以下步骤。

流程

  1. 打开 Admin Console 并通过输入 admin 用户名和密码登录。

    登录管理控制台后,将有一个现有的域。当首次设置根域 master 时,会默认创建 Red Hat build of Keycloak。任何之前创建的域都会在下拉列表中的 Admin Console 的左上角列出。

  2. 从 realm 下拉列表中,选择 Add realm
  3. 在 Name 字段中键入 test_realm,然后单击 Create

在 Red Hat build of Keycloak SAML SP 中,称为客户端。要添加 SP,我们必须在域的 Clients 部分中。

  1. 点左侧的 Clients 菜单项,然后点 Import client 按钮。
  2. Resource file 字段中,提供上面创建的 Mellon SP 元数据文件(/etc/httpd/saml2/mellon_metadata.xml)。

    根据浏览器的运行位置,您可能需要将 SP 元数据从 $sp_host 复制到运行浏览器的计算机,以便浏览器可以找到该文件。

  3. 点击 Save
8.1.6.2. 编辑 Mellon SP 客户端

使用这个流程设置重要的客户端配置参数。

流程

  1. 确保 Force POST Binding 为 On。
  2. 将 paosResponse 添加到 Valid Redirect URIs 列表中:
  3. 复制 Valid Redirect URI 中的 postResponse URL,并将它粘贴到 "+" 下的空添加文本字段中。
  4. postResponse 更改为 paosResponse'。(SAML ECP 需要 paosResponse URL。)
  5. 点底部的 Save

许多 SAML SP 根据组中的用户成员资格决定授权。红帽 Keycloak IdP 的构建可以管理用户组信息,但不会提供用户组,除非 IdP 配置为将其作为 SAML 属性提供。

执行以下步骤将 IdP 配置为将用户组作为 SAML 属性提供。

流程

  1. 点客户端的 Client scopes 选项卡。
  2. 点 dedicated 范围放置在第一行中。
  3. 在 Mappers 页面中,单击 Add mapper 按钮并选择 By configuration
  4. 从 Mapper Type 列表中,选择 Group 列表
  5. 将 Name 设置为 组列表
  6. 将 SAML 属性名称设置为
  7. 点击 Save

剩余的步骤在 $sp_host 上执行。

8.1.6.3. 检索身份提供程序元数据

现在,您已在 IdP 上创建域,您需要检索与之关联的 IdP 元数据,以便 Mellon SP 识别它。在之前创建的 /etc/httpd/conf.d/mellon.conf 文件中,MellonIdPMetadataFile 指定为 /etc/httpd/saml2/idp_metadata.xml,直到该文件在 $sp_host 上不存在。

使用这个流程从 IdP 检索该文件。

流程

  1. 使用这个命令,使用 $idp_host 的正确值替换:

    curl -k -o /etc/httpd/saml2/idp_metadata.xml \
    https://$idp_host/realms/test_realm/protocol/saml/descriptor

    Mellon 现已全面配置。

  2. 要运行 Apache 配置文件的语法检查,请使用以下命令:

    apachectl configtest
    注意

    configtest 等同于 apachectl 的 -t 参数。如果配置测试显示任何错误,请在继续操作前进行更正。

  3. 重启 Apache 服务器:

    systemctl restart httpd.service

现在,您已在 test_realm 和 mod_auth_mellon 中将红帽构建的 Keycloak 设置为 SAML IdP,通过针对 $idp_host IdP 进行身份验证来保护 URL $sp_host/protected (及其下的所有内容)。

第 9 章 配置 Docker registry

配置 Docker registry 以使用红帽构建的 Keycloak。

注意

Docker 身份验证默认为禁用。要启用和禁用功能,请参阅 启用和禁用功能

本节论述了如何配置 Docker registry,以使用红帽构建的 Keycloak 作为其身份验证服务器。

有关如何设置和配置 Docker registry 的更多信息,请参阅 Docker 注册表配置指南

9.1. 安装 Docker registry 配置文件

对于具有更高级的 Docker registry 配置的用户,通常建议提供自己的 registry 配置文件。Red Hat build of Keycloak Docker 供应商通过 Registry Config File Format 选项支持这个机制。选择这个选项将生成类似如下的输出:

auth:
  token:
    realm: http://localhost:8080/realms/master/protocol/docker-v2/auth
    service: docker-test
    issuer: http://localhost:8080/realms/master

然后可将此输出复制到任何现有的 registry 配置文件中。如需有关如何 设置文件的更多信息,或以 基本示例 开始,请参阅 registry 配置文件规格

警告

别忘记使用 Red Hat build of Keycloak 域的公钥的位置配置 rootcertbundle 字段。没有此参数,auth 配置将无法正常工作。

9.2. Docker registry 环境变量覆盖安装

通常,最好在开发或 POC Docker registry 中使用简单的环境变量覆盖。虽然这种方法通常不建议在生产环境中使用,但当一个要求快速和dirty 方法来备份 registry 时,这很有用。只需使用客户端详情中的 Variable Override 格式选项,输出应如下所示:

REGISTRY_AUTH_TOKEN_REALM: http://localhost:8080/realms/master/protocol/docker-v2/auth
REGISTRY_AUTH_TOKEN_SERVICE: docker-test
REGISTRY_AUTH_TOKEN_ISSUER: http://localhost:8080/realms/master
警告

别忘记使用红帽构建的 Keycloak 域公钥的位置配置 REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE 覆盖。没有此参数,auth 配置将无法正常工作。

9.3. Docker Compose YAML 文件

警告

这个安装方法旨在获得针对红帽构建的 Keycloak 服务器进行身份验证的 docker registry 的一种简单方法。它仅用于开发目的,不应用于生产环境或类似生产环境的环境中。

zip 文件安装机制为希望了解红帽构建的 Keycloak 服务器可以与 Docker 注册表交互的开发人员提供了一个快速入门。要配置:

流程

  1. 从所需的域中,创建客户端配置。此时,您没有 Docker registry - Quickstart 将处理该部分。
  2. Action 菜单中选择 Docker Compose YAML 选项,然后选择 Download adapter 配置选项来下载 ZIP 文件。
  3. 将存档解压缩到所需位置,然后打开 目录。
  4. 使用 docker-compose 启动Docker registry
注意

建议您在 'master' 以外的域中配置 Docker registry 客户端,因为 HTTP 基本身份验证流不存在表单。

上述配置发生,并且 keycloak 服务器和 Docker registry 正在运行后,docker 身份验证应该可以成功:

[user ~]# docker login localhost:5000 -u $username
Password: *******
Login Succeeded

第 10 章 使用客户端注册服务

使用客户端注册服务。

要让应用程序或服务使用红帽构建的 Keycloak,必须在红帽构建的 Keycloak 中注册客户端。管理员可以通过管理控制台(或 admin REST 端点),但客户端也可以通过红帽构建的 Keycloak 客户端注册服务自行注册。

客户端注册服务提供对红帽构建的 Keycloak Client Representations、OpenID Connect Client Meta Data 和 SAML Entity Descriptors 的内置支持。客户端注册服务端点为 /realms/<realm>/clients-registrations/<provider&gt;。

内置支持的 供应商 有:

  • Default - Red Hat build of Keycloak Clientpresentation (JSON)
  • Install - 红帽构建的 Keycloak Adapter 配置(JSON)
  • openid-connect - OpenID Connect Client Metadata Description (JSON)
  • saml2-entity-descriptor - SAML Entity Descriptor (XML)

以下小节将描述如何使用不同的提供程序。

10.1. 身份验证

要调用客户端注册服务,您需要令牌。令牌可以是 bearer 令牌、初始访问令牌或注册访问令牌。另外,还可以在没有任何令牌的情况下注册新客户端,但随后您需要配置客户端注册策略(请参阅以下)。

10.1.1. bearer 令牌

bearer 令牌可代表用户或服务帐户发布。调用端点需要以下权限( 更多详情请参阅服务器管理指南 ):

  • create-client 或 manage-client - 要创建客户端
  • view-client 或 manage-client - 要查看客户端
  • manage-client - 要更新或删除客户端

如果您使用 bearer 令牌来创建客户端,建议只使用 create-client 角色的服务帐户中的令牌(更多详情请参阅 服务器管理指南 )。

10.1.2. 初始访问令牌

注册新客户端的建议方法是使用初始访问令牌。初始访问令牌只能用于创建客户端,并且具有可配置的过期时间以及可创建客户端数量的可配置限制。

可以通过管理控制台创建初始访问令牌。要创建新的初始访问令牌,首先在 admin 控制台中选择域,然后在左侧的菜单中单击 Client,然后在页面中显示的标签页中单击 Initial access token

现在,您可以看到任何现有的初始访问令牌。如果您有访问权限,您可以删除不再需要的令牌。您只能在创建令牌时检索令牌的值。要创建新令牌,请点 Create。现在,您可以选择添加令牌应有效的时长,也可以使用令牌创建多少个客户端。点 Save the token 值后会显示。

务必要现在复制/粘贴此令牌,因为您在以后无法检索此令牌。如果您忘记复制/粘贴,请删除该令牌并创建另一个令牌。

在调用 Client Registration Services 时,令牌值用作标准 bearer 令牌,方法是将其添加到请求中的 Authorization 标头中。例如:

Authorization: bearer eyJhbGciOiJSUz...

10.1.3. 注册访问令牌

当您通过客户端注册服务创建客户端时,响应将包括注册访问令牌。注册访问令牌提供对稍后检索客户端配置的访问权限,但也要更新或删除客户端。注册访问令牌包含在请求中,其方式与 bearer 令牌或初始访问令牌相同。

默认情况下启用注册访问令牌轮转。这意味着注册访问令牌仅有效一次。使用令牌时,响应将包括新令牌。请注意,可以使用客户端策略禁用注册访问令牌 轮转

如果在客户端注册服务之外创建客户端,它没有与之关联的注册访问令牌。您可以通过 admin 控制台创建一个。如果您丢失了特定客户端的令牌,这也很有用。若要创建新令牌,请在 admin 控制台中查找客户端,再单击 Credentials。然后,单击 Generate registration access token

10.2. Red Hat build of Keycloak Representations

默认 客户端注册提供程序可用于创建、检索、更新和删除客户端。它使用红帽构建的 Keycloak Client Representation 格式,它提供对配置客户端的支持,就像通过管理控制台配置客户端一样,包括配置协议映射程序。

要创建客户端,请创建一个 Client Representation (JSON),然后对 /realms/<realm>/clients-registrations/default 执行 HTTP POST 请求。

它将返回一个包括注册访问令牌的 Client Representation。如果要检索配置,请稍后更新或删除客户端,您应该在某个地方保存注册访问令牌。

要检索 Client Representation,请执行对 /realms/<realm>/clients-registrations/default/<client id> 的 HTTP GET 请求。

它还会返回一个新的注册访问令牌。

要更新 Client Representation,请执行带有更新的 Client Representation to: /realms/<realm>/clients-registrations/default/<client id > 的 HTTP PUT 请求。

它还会返回一个新的注册访问令牌。

要删除 Client Representation 执行 HTTP DELETE 请求: /realms/<realm>/clients-registrations/default/<client id>

10.3. Red Hat build of Keycloak 适配器配置

安装 客户端注册供应商可用于检索客户端的适配器配置。除了令牌身份验证外,您还可以使用 HTTP 基本身份验证通过客户端凭证进行身份验证。要做到这一点在请求中包含以下标头:

Authorization: basic BASE64(client-id + ':' + client-secret)

要检索适配器配置,然后对 /realms/<realm>/clients-registrations/install/<client id > 执行 HTTP GET 请求。

公共客户端不需要身份验证。这意味着,对于 JavaScript 适配器,您可以使用上述 URL 从红帽构建的 Keycloak 直接加载客户端配置。

10.4. OpenID Connect Dynamic Client Registration

红帽 Keycloak 的构建实施 OpenID Connect Dynamic Client Registration,它扩展了 OAuth 2.0 Dynamic Client Registration ProtocolOAuth 2.0 Dynamic Client Registration Management 协议

在 Red Hat build of Keycloak 中注册客户端的端点是 /realms/<realm>/clients-registrations/openid-connect[/<client id>]

此端点也可以在 realm 的 OpenID Connect Discovery 端点 /realms/<realm>/.well-known/openid-configuration 中找到。

10.5. SAML 实体描述符

SAML Entity Descriptor 端点只支持使用 SAML v2 实体描述符来创建客户端。它不支持检索、更新或删除客户端。对于这些操作,应使用红帽构建的 Keycloak 表示端点。在创建红帽构建的 Keycloak Client Representation 时,会返回有关创建客户端的详细信息,包括注册访问令牌。

要创建带有 SAML Entity Descriptor 到 /realms/<realm>/clients-registrations/saml2-entity-descriptor 的 HTTP POST 请求,请执行以下操作:

10.6. 使用 CURL 的示例

以下示例使用 CURL 创建带有 clientId myclient 的客户端。您需要将 eyJhbGciOiJSUz…​ 替换为正确的初始访问令牌或 bearer 令牌。

curl -X POST \
    -d '{ "clientId": "myclient" }' \
    -H "Content-Type:application/json" \
    -H "Authorization: bearer eyJhbGciOiJSUz..." \
    http://localhost:8080/realms/master/clients-registrations/default

10.7. 使用 Java 客户端注册 API 的示例

客户端注册 Java API 可以轻松地使用 Java 客户端注册服务。要使用包括依赖项 org.keycloak:keycloak-client-registration-api:>VERSION< 来自 Maven。

有关使用客户端注册的完整说明,请参阅 JavaDocs。以下是创建客户端的示例。您需要将 eyJhbGciOiJSUz…​ 替换为正确的初始访问令牌或 bearer 令牌。

String token = "eyJhbGciOiJSUz...";

ClientRepresentation client = new ClientRepresentation();
client.setClientId(CLIENT_ID);

ClientRegistration reg = ClientRegistration.create()
    .url("http://localhost:8080", "myrealm")
    .build();

reg.auth(Auth.token(token));

client = reg.create(client);

String registrationAccessToken = client.getRegistrationAccessToken();

10.8. 客户端注册策略

注意

当前计划正在删除客户端注册策略,以取代 服务器管理指南 中所述的客户端策略。客户端策略更为灵活,支持更多用例。

Red Hat build of Keycloak 目前支持通过客户端注册服务注册新客户端的两个方式。

  • 经过身份验证的请求 - 注册新客户端的 Request 必须包含 Initial Access TokenBearer Token (如上所述)。
  • 匿名请求 - 请求注册新客户端不需要包含任何令牌

匿名客户端注册请求非常有趣且强大的功能,但您通常不希望任何人都可以在没有限制的情况下注册新客户端。因此,我们有 客户端注册策略 SPI,它提供了一种限制谁可以注册新客户端以及哪些条件下。

在 Red Hat build of Keycloak admin console 中,您可以点 Client Registration 选项卡,然后点 Client Registration Policies 子选项卡。在这里,您将看到为匿名请求默认配置哪些策略,以及为经过身份验证的请求配置哪些策略。

注意

仅允许创建新客户端创建(注册)匿名请求(无令牌请求)。因此,当您通过匿名请求注册新客户端时,响应将包含 Registration Access Token,它必须用于特定客户端的 Read、Update 或 Delete 请求。但是,从匿名注册使用此注册访问令牌也会受到匿名策略的影响!这意味着,如果您有受信任的主机策略,更新客户端的请求还需要来自 受信任的主机。例如,在更新客户端时以及存在 Consent Required 策略时,不允许禁用 Consent Required

目前,我们有这些策略实现:

  • 可信主机策略 - 您可以配置可信主机和可信域的列表。只能从那些主机或域向客户端注册服务发送请求。从某些不受信任的 IP 发送的请求将被拒绝。新注册的客户端的 URL 还必须只使用那些可信主机或域。例如,不允许设置指向某些不受信任的主机的客户端 重定向 URI。默认情况下,没有任何白名单的主机,因此匿名客户端注册被禁用。
  • 同意所需策略 - 新注册的客户端将启用 Consent Allowed 开关。因此,在身份验证成功后,用户会在需要批准权限(客户端范围)时始终看到同意屏幕。这意味着,除非用户批准,否则客户端无法访问任何个人信息或用户权限。
  • 协议映射程序策略 - 允许配置白名单协议映射器实施列表。如果新客户端包含一些非whitelist 的协议映射程序,则无法注册或更新。请注意,此策略也用于经过身份验证的请求,因此即使经过身份验证的请求也有一些限制,可以使用协议映射程序。
  • 客户端范围策略 - 允许将 客户端范围 列入白名单,可用于新注册或更新的客户端。默认没有白名单范围;只有客户端范围定义为 Realm Default Client Scopes,默认会列入白名单。
  • full Scope Policy - 新注册的客户端将禁用 Full Scope Allowed switch。这意味着它们没有任何其他客户端的任何范围域角色或客户端角色。
  • Max Clients Policy - 如果域中当前客户端数量相同或大于指定限制,则拒绝注册。默认情况下,匿名注册为 200。
  • Client Disabled Policy - 新注册的客户端将被禁用。这意味着 admin 需要手动批准并启用所有新注册的客户端。默认不使用此策略,即使进行匿名注册。

第 11 章 使用 CLI 自动注册客户端

使用 CLI 自动注册客户端。

客户端注册 CLI 是应用程序开发人员在与红帽构建的 Keycloak 集成时,用来以自助服务方式配置新客户端的命令行界面(CLI)工具。它旨在与红帽构建的 Keycloak 客户端注册 REST 端点交互。

必须为任何应用程序创建或获取客户端配置,才能使用红帽构建的 Keycloak。您通常会为托管在唯一主机名的每个新应用配置新客户端。当应用程序与红帽构建的 Keycloak 交互时,应用程序会用客户端 ID 标识自己,因此红帽构建的 Keycloak 可以提供登录页面、单点登录(SSO)会话管理和其他服务。

您可以使用 客户端注册 CLI 从命令行配置应用程序客户端,您可以在 shell 脚本中使用它。

要允许特定用户使用 客户端注册 CLI,红帽构建的 Keycloak 管理员通常使用 Admin Console 配置具有适当角色的新用户,或者配置新客户端和客户端 secret 以授予客户端注册 REST API 的访问权限。

流程

  1. admin 用户身份登录管理控制台(例如 http://localhost:8080)。
  2. 选择要管理的域。
  3. 如果要使用现有用户,请选择该用户进行编辑;否则,创建一个新用户。
  4. 选择 角色映射分配角色。在选项列表中,单击 Filter by client。在搜索栏中,键入 manage-clients。选择角色,或者您位于 master 域中,请选择带有 NAME-realm 的文件,其中 NAME 是目标域的名称。您可以为 master 域中的用户授予任何其他域的访问权限。
  5. 单击 Assign 以授予一组完整的客户端管理权限。另一个选择是,为只读选择 view-clientscreate-client 来创建新客户端。
  6. 选择 Available Roles,manage-client 来授予一组完整的客户端管理权限。另一个选择是,为只读选择 view-clientscreate-client 来创建新客户端。

    注意

    这些权限授予用户在没有使用 Initial Access Token 或 Registration Access Token 的情况下执行操作的能力(如需更多信息 ,请参阅 使用客户端注册服务 )。

无法为用户分配任何 realm-management 角色。在这种情况下,用户仍可使用 客户端注册 CLI 登录,但在没有 Initial Access Token 的情况下无法使用它。在没有令牌的情况下尝试执行任何操作会导致 403 Forbidden 错误。

管理员可以从 Initial Access Token 选项卡中的 Clients 区域发布 Initial Access Tokens。

11.2. 配置客户端以用于客户端注册 CLI

默认情况下,服务器将客户端注册 CLI 识别为 admin-cli 客户端,该客户端会自动为每个新域配置。使用用户名登录时不需要额外的客户端配置。

流程

  1. 如果要为客户端注册 CLI 使用单独的客户端配置,请创建一个客户端(例如 reg-cli)。
  2. 取消选中 Standard Flow Enabled
  3. 通过将 客户端身份验证切换为 On 来增强安全性。
  4. 选择要使用的帐户类型。

    1. 如果要使用与客户端关联的服务帐户,请检查 服务帐户角色
    2. 如果您希望使用常规用户帐户,选中 直接访问会授予
  5. Next
  6. 点击 Save
  7. Credentials 选项卡。

    配置 Client Id 和 SecretSigned JWT

  8. 如果使用服务帐户角色,点 Service Account Roles 选项卡。

    选择要配置服务帐户访问权限的角色。有关选择的角色的详情,请参考 第 11.1 节 “配置一个新的常规用户以用于客户端注册 CLI”

  9. 点击 Save

运行 kcreg config credentials 时,请使用-- secret 选项来提供配置的 secret。

  • 在运行 kcreg config credentials 时,指定要使用哪个 clientId (例如,--client reg-cli)。
  • 启用服务帐户后,您可以在运行 kcreg 配置凭证时 省略指定用户,并且仅提供客户端 secret 或密钥存储信息。

11.3. 安装客户端注册 CLI

客户端注册 CLI 打包在红帽构建 Keycloak 服务器分发中。您可以在 bin 目录中找到执行脚本。Linux 脚本名为 kcreg.sh,Windows 脚本名为 kcreg.bat

在设置客户端以便在文件系统的任何位置使用时,将 Red Hat build of Keycloak 服务器目录添加到您的 PATH 中。

例如,在:

  • Linux:
$ export PATH=$PATH:$KEYCLOAK_HOME/bin
$ kcreg.sh
  • Windows:
c:\> set PATH=%PATH%;%KEYCLOAK_HOME%\bin
c:\> kcreg

KEYCLOAK_HOME 是指红帽构建的 Keycloak Server 发行版解压缩的目录。

11.4. 使用客户端注册 CLI

流程

  1. 使用您的凭证登录来启动一个经过身份验证的会话。
  2. 客户端注册 REST 端点上 运行命令。

    例如,在:

    • Linux:

      $ kcreg.sh config credentials --server http://localhost:8080 --realm demo --user user --client reg-cli
      $ kcreg.sh create -s clientId=my_client -s 'redirectUris=["http://localhost:8980/myapp/*"]'
      $ kcreg.sh get my_client
    • Windows:

      c:\> kcreg config credentials --server http://localhost:8080 --realm demo --user user --client reg-cli
      c:\> kcreg create -s clientId=my_client -s "redirectUris=[\"http://localhost:8980/myapp/*\"]"
      c:\> kcreg get my_client
      注意

      在生产环境中,必须使用 https: 访问红帽构建的 Keycloak:以避免向网络嗅探器公开令牌。

  3. 如果服务器证书不是由 Java 默认证书信任存储中包含的可信证书颁发机构(CA)发布的,准备 truststore.jks 文件,并指示客户端注册 CLI 使用它。

    例如,在:

    • Linux:

      $ kcreg.sh config truststore --trustpass $PASSWORD ~/.keycloak/truststore.jks
    • Windows:

      c:\> kcreg config truststore --trustpass %PASSWORD% %HOMEPATH%\.keycloak\truststore.jks

11.4.1. 登录

流程

  1. 当您使用客户端注册 CLI 登录时,指定服务器端点 URL 和域。
  2. 指定用户名或客户端 id,这会导致使用特殊服务帐户。在使用用户名时,您必须为指定用户使用密码。使用客户端 ID 时,请使用客户端 secret 或 Signed JWT 而不是密码。

无论登录方法如何,登录的帐户都需要适当的权限才能执行客户端注册操作。请记住,非 master 域中的任何帐户都只能有管理同一域中的客户端的权限。如果需要管理不同的域,您可以在不同的域中配置多个用户,或者在 master 域中创建单个用户,并添加用于管理不同域中客户端的角色。

您不能使用客户端注册 CLI 配置用户。使用 Admin Console Web 界面或 Admin Client CLI 来配置用户。如需了解更多详细信息,请参阅 服务器管理指南

kcreg 成功登录时,它会接收授权令牌并将其保存在私有配置文件中,以便令牌可用于后续调用。有关配置文件的更多信息,请参阅 第 11.4.2 节 “使用其他配置”

有关使用客户端注册 CLI 的更多信息,请参阅内置帮助。

例如,在:

  • Linux:
$ kcreg.sh help
  • Windows:
c:\> kcreg help

有关启动经过身份验证的会话的更多信息,请参阅 kcreg config credentials --help

11.4.2. 使用其他配置

默认情况下,Client Registration CLI 会在用户的主目录下自动维护一个默认位置 ./.keycloak/kcreg.config 的配置文件。您可以使用-- config 选项指向不同的文件或位置,以并行维护多个经过身份验证的会话。执行从一个线程绑定到单个配置文件的操作是最安全的。

重要

不要使配置文件对系统上的其他用户可见。配置文件 包含应保持私有的访问令牌和 secret。

您可能希望通过在所有命令中使用 --no-config 选项避免将 secret 存储到配置文件中,即使它不太方便,并且需要更多令牌请求才能这样做。使用每个 kcreg 调用指定所有身份验证信息。

11.4.3. 初始访问和注册访问令牌

在它们要使用的红帽构建的 Keycloak 服务器中没有配置帐户的开发人员可以使用客户端注册 CLI。只有域管理员向开发人员发出初始访问令牌时,才能执行此操作。域管理员由域管理员决定如何发布和发布这些令牌。域管理员可以限制 Initial Access Token 的最长期限,以及可通过它创建的客户端总数。

开发人员具有 Initial Access Token 后,开发人员可以使用它来创建新客户端,而无需使用 kcreg 配置凭证进行身份验证。Initial Access Token 可以存储在配置文件中,也可以指定为 kcreg create 命令的一部分。

例如,在:

  • Linux:
$ kcreg.sh config initial-token $TOKEN
$ kcreg.sh create -s clientId=myclient

或者

$ kcreg.sh create -s clientId=myclient -t $TOKEN
  • Windows:
c:\> kcreg config initial-token %TOKEN%
c:\> kcreg create -s clientId=myclient

或者

c:\> kcreg create -s clientId=myclient -t %TOKEN%

使用 Initial Access Token 时,服务器响应包括新发布的注册访问令牌。对该客户端的任何后续操作都需要通过使用该令牌进行身份验证来执行,这仅对该客户端有效。

客户端注册 CLI 自动使用其专用配置文件来保存并使用此令牌及其关联的客户端。只要将相同的配置文件用于所有客户端操作,开发人员就不需要进行身份验证来读取、更新或删除以这种方式创建的客户端。

有关初始访问和注册访问令牌的更多信息,请参阅使用 客户端注册服务

运行 kcreg config initial-token --helpkcreg config registration-token --help 命令,以了解有关如何使用客户端注册 CLI 配置令牌的更多信息。

11.4.4. 创建客户端配置

使用凭据进行身份验证或配置 Initial Access Token 后的第一项任务通常是创建新客户端。通常,您可能希望使用准备好的 JSON 文件作为模板,并设置或覆盖其中的一些属性。

以下示例演示了如何读取 JSON 文件,覆盖它可能包含的任何客户端 ID,设置任何其他属性,并在成功创建后将配置打印到标准输出。

  • Linux:
$ kcreg.sh create -f client-template.json -s clientId=myclient -s baseUrl=/myclient -s 'redirectUris=["/myclient/*"]' -o
  • Windows:
C:\> kcreg create -f client-template.json -s clientId=myclient -s baseUrl=/myclient -s "redirectUris=[\"/myclient/*\"]" -o

运行 kcreg create --help 来了解有关 kcreg create 命令的更多信息。

您可以使用 kcreg attrs 来列出可用的属性。请记住,许多配置属性不会检查有效或一致性。您最多可指定正确的值。请记住,您的模板中不应有任何 id 字段,不应将它们指定为 kcreg create 命令的参数。

11.4.5. 检索客户端配置

您可以使用 kcreg get 命令检索现有客户端。

例如,在:

  • Linux:
$ kcreg.sh get myclient
  • Windows:
C:\> kcreg get myclient

您还可以将客户端配置作为适配器配置文件检索,您可以使用 web 应用程序打包该文件。

例如,在:

  • Linux:
$ kcreg.sh get myclient -e install > keycloak.json
  • Windows:
C:\> kcreg get myclient -e install > keycloak.json

运行 kcreg get --help 命令来了解有关 kcreg get 命令的更多信息。

11.4.6. 修改客户端配置

更新客户端配置的方法有两种。

种方法是在获取当前配置后向服务器提交完整的新状态,将它保存到文件中,编辑文件,并将它回发给服务器。

例如,在:

  • Linux:
$ kcreg.sh get myclient > myclient.json
$ vi myclient.json
$ kcreg.sh update myclient -f myclient.json
  • Windows:
C:\> kcreg get myclient > myclient.json
C:\> notepad myclient.json
C:\> kcreg update myclient -f myclient.json

第二种方法获取当前客户端、设置或删除字段,并将其重新置于一个步骤中。

例如,在:

  • Linux:
$ kcreg.sh update myclient -s enabled=false -d redirectUris
  • Windows:
C:\> kcreg update myclient -s enabled=false -d redirectUris

您还可以使用仅包含要应用更改的文件,因此您不必将太多的值指定为参数。在这种情况下,使用 specify- merge 来告知客户端注册 CLI,而不是将 JSON 文件视为完整、新配置,应该将其视为一组要应用到现有配置的属性。

例如,在:

  • Linux:
$ kcreg.sh update myclient --merge -d redirectUris -f mychanges.json
  • Windows:
C:\> kcreg update myclient --merge -d redirectUris -f mychanges.json

运行 kcreg update --help 命令来了解有关 kcreg update 命令的更多信息。

11.4.7. 删除客户端配置

使用以下示例删除客户端。

  • Linux:
$ kcreg.sh delete myclient
  • Windows:
C:\> kcreg delete myclient

运行 kcreg delete --help 命令来了解有关 kcreg delete 命令的更多信息。

11.4.8. 刷新无效的注册访问令牌

当使用- no-config 模式执行创建、读取、更新和删除(CRUD)操作时,客户端注册 CLI 无法为您处理注册访问令牌。在这种情况下,可以丢失对客户端最近发布的注册访问令牌的跟踪,因此无法在该客户端上执行任何进一步的 CRUD 操作,而无需使用具有 管理客户端权限的账户 进行身份验证。

如果您有权限,您可以向客户端发出新的注册访问令牌,并将其打印到标准输出或保存到您选择的配置文件中。否则,您必须要求域管理员为客户端发出一个新的注册访问令牌,并将其发送给您。然后,您可以通过 the- token 选项将其传递给任何 CRUD 命令。您还可以使用 kcreg config registration-token 命令将新令牌保存到配置文件中,并让客户端注册 CLI 会自动从那个点处理它。

运行 kcreg update-token --help 命令来了解有关 kcreg update-token 命令的更多信息。

11.5. 故障排除

  • 问:登录时,收到错误: Parameter client_assertion_type is missing [invalid_client].

    答:此错误表示您的客户端配置了 Signed JWT 令牌凭证,这意味着您必须在登录时使用-- keystore 参数。

第 12 章 配置和使用令牌交换

配置和使用令牌交换红帽构建的 Keycloak。

令牌交换是允许客户端应用程序为另一个令牌交换一个令牌的过程。在 Red Hat build of Keycloak 中,两个功能实现了令牌交换:

红帽构建的 Keycloak 用于令牌交换的功能如下:

  1. 客户端可以交换为特定客户端创建用于同一域中不同客户端的新令牌的现有红帽构建的 Keycloak 令牌。
  2. 客户端可以为外部令牌交换现有红帽构建的 Keycloak 令牌,如链接的 Facebook 帐户。
  3. 客户端可以为红帽构建的 Keycloak 令牌交换外部令牌。
  4. 客户端可以模拟用户。

标准令牌交换仅支持用例(1)。传统令牌交换支持四个用例,但它是一个技术预览功能。因此,建议标准令牌交换 V2,因为它被支持,并将在以后维护。传统令牌交换对最后三个用例很有用,但可能不向后兼容未来的红帽构建的 Keycloak 版本。您还可以启用令牌交换功能,并将它们一起使用。例如,您可以将 V2 提供的内部内部交换与 V1 支持的其他用例一起使用。如需了解更多详细信息,请参阅此 令牌交换比较

注意

如果您仍然需要旧的令牌交换功能,则还需要启用 Fine-grained admin permissions version 1 (FGAP:v 1),因为版本 2 (FGAP:v2) 不支持令牌交换权限。这是因为 token-exchange 是概念上并不是"管理员"权限,因此没有计划将令牌交换权限添加到 FGAP:v2。

12.1. 标准令牌交换

红帽构建的 Keycloak 中的标准令牌交换实现了 Token Exchange 规格。它允许客户端应用程序交换为签发的新令牌到触发令牌交换请求的客户端的特定客户端创建的 Keycloak 令牌的现有红帽构建。两个客户端都必须位于同一域中。

12.1.1. 令牌交换流

考虑这个典型的令牌交换流:

  1. 用户使用红帽构建的 Keycloak SSO 进行身份验证到客户端应用 初始客户端。该令牌将发布到 初始客户端
  2. 客户端 initial-client 可能需要使用 REST 服务 requester-client,这需要身份验证。因此,initial-client 使用令牌将访问令牌从第 1 步发送到 requester-client
  3. 为了服务请求,requester-client 可能需要调用另一个服务 target-client。但是,它可能无法使用从 initial-client 发送到令牌。例如:

    • 令牌的权限或范围不足。
    • target-client 没有被指定为令牌 audience;令牌旨在用于调用 requester-client
    • 令牌具有太多权限,因此 requester-client 可能不希望与 target-client 共享。

    这样的情形可能是调用令牌交换的原因。requester-client 可能需要向红帽构建的 Keycloak 服务器发送令牌交换请求,并使用第 1 步中的原始令牌作为 主题令牌,并为 请求的 另一个令牌交换它。

  4. 请求的令牌 返回到 requester-client。此令牌现在可以发送到 target-client
  5. target-client 可以满足请求,并将响应返回给 requester-client。然后,requester-client 可以跟进,并将响应返回在第 2 步中的请求。

令牌交换存在许多其他用例,但前面的示例是最典型的。

12.1.1.1. 令牌交换请求示例

以下是域 test 中客户端 请求者-client 的令牌交换请求示例。请注意,subject_token 是向 initial-client 发布的访问令牌:

POST /realms/test/protocol/openid-connect/token
Authorization: Basic cmVxdWVzdGVyLWNsaWVudDpwYXNzd29yZA==
Content-Type: application/x-www-form-urlencoded
Accept: application/json

grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token=$SUBJECT_TOKEN&
subject_token_type=urn:ietf:params:oauth:token-type:access_token&
requested_token_type=urn:ietf:params:oauth:token-type:access_token

令牌交换响应示例可能类似如下:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsIn...",
  "expires_in": 300,
  "token_type": "Bearer",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "session_state": "287f3c57-32b8-4c0f-8b00-8c7db231d701",
  "scope": "default-scope1",
  "refresh_expires_in": 0,
  "not-before-policy": 0
}

12.1.2. 如何启用令牌交换

对于标准令牌交换,token-exchange-standard:v2 会被默认启用。但是,您还需要为应当发送 令牌交换请求的客户端启用标准 令牌交换交换机,如 上例中的 requester-client。请注意,requester-client 必须是一个机密客户端。另外,与其他授权请求一样,令牌交换请求必须通过为客户端配置的适当 客户端验证方法进行身份验证

图 12.1. 启用令牌交换

启用令牌交换

12.1.3. 请求和响应参数

参数与 Token Exchange 规格 一致,如下:

grant_type
必需。参数的值必须是 urn:ietf:params:oauth:grant-type:token-exchange
subject_token
必需。代表要发出请求的人,代表方身份的安全令牌。
subject_token_type
必需。此参数是在 subject_token 参数中传递的令牌类型。当使用标准令牌交换时,必须为 urn:ietf:params:oauth:token-type:access_token,因为红帽构建的 Keycloak 不支持标准令牌交换的其他类型的。
requested_token_type
可选。此参数代表了客户端要交换的令牌类型。在本发行版本中,只支持 oauth 和 OpenID Connect 令牌类型。默认值为 urn:ietf:params:oauth:token-type:access_token。如果请求了请求给 requester-client 的 ID 令牌,则另一个可能的值是 urn:ietf:params:oauth:token-type:id_token。可能的值可能是 urn:ietf:params:oauth:token-type:refresh_token; 在这种情况下,您将在响应中接收访问令牌和刷新令牌。但是,如果在 Standard Token Exchange 客户端配置选项中启用了 Allow refresh token,则允许刷新令牌,如 标准令牌交换 部分中指定的。
scope
可选。此参数代表客户端请求的以空格分隔的 OAuth 和 OpenID Connect 范围集合。您可以使用 requester- client 的可选客户端范围。如需了解更多详细信息,请参阅 范围和受众。省略此参数意味着,只会有效地使用 Default 客户端范围
受众
可选。audience 指定客户端的 client_id,它应当用作令牌受众。在上例中, 它可以是 target-client。这个参数的多个值是允许的,这意味着您希望令牌包含多个不同服务的 requester-client 使用。例如 audience=target-client1&audience=target-client2 可以在请求中使用。有关 范围和受众的部分中的更多详细信息

JSON 格式返回成功响应。它包含类似的参数,如来自其他授权的响应。以下是更显著参数的一些令牌交换细节:

access_token
请求的访问令牌。请注意,如果请求指定了 requested_token_type=urn:ietf:params:oauth:token-type:id_token,则此参数实际上可能包含 ID 令牌而不是访问令牌。此行为根据 令牌交换规格
refresh_token
刷新令牌。只有在使用 requested_token_type=urn:ietf:params:oauth:token-type:refresh_token 时包括它,且客户端已经启用了从令牌交换发出刷新令牌
issued_token_type
发布的令牌类型。值与请求中使用的 requested_token_type 相同。
token_type
通常,如果发布的令牌类型是访问令牌或刷新令牌,则通常是 Bearer。如果请求的 ID 令牌,则值为 N_A

12.1.4. 范围和受众

令牌交换请求中的 scope 参数与其他授予的含义相同。这个参数是可选的。当省略时,请求中使用的有效客户端范围是 requester- client 的 Default 客户端范围。使用此参数时,有效的客户端范围是默认范围和可选 客户端范围

默认情况下,使用的客户端范围将根据 Audience 文档 中指定的已用客户端范围和客户端角色将使用者添加到令牌的声明中。

audience 参数可用于过滤受众,以便 aud 声明将仅包含 audience 参数指定的使用者。类似地,令牌中的客户端角色将被过滤,令牌将仅具有由 audience 参数指定的客户端的客户端角色。

此外,audience 参数也可用于可能过滤客户端范围。它的工作方式类似于 用户 的客户端范围权限。如果客户端范围不包含任何客户端角色(例如,它包含零角色或仅包含 realm 角色),则不会对客户端范围进行额外的过滤。但是,如果客户端范围包含任何客户端角色映射,它必须包含 audience 参数所请求的客户端的一些客户端角色。还包括了复合角色以供考虑。如果客户端范围不包含 audience 所请求的客户端的客户端角色,则会过滤客户端范围。

注意

audience 参数可用于过滤来自已用客户端范围的受众。但是,此参数不会添加更多受众。当省略 audience 参数时,不会发生过滤。因此,audience 参数有效用于"关闭"令牌,以确保它只包含请求的受众。但是,scope 参数用于添加可选的客户端范围,因此可用于"upscoping"并添加更多范围。

12.1.4.1. 例子

下面是一些示例,以更好地说明范围和受众的行为。

假设有带有以下内容的域:

  • 带有客户端角色 target-client1-role的客户端 target-client1
  • 带有客户端角色 target-client2-role的客户端 target-client2
  • 带有客户端角色 target-client3-role的客户端 target-client3
  • 客户端范围 default-scope1。此客户端范围具有客户端角色 target-client1/target-client1-role的角色范围映射
  • 客户端范围 optional-scope2.此客户端范围具有客户端角色 target-client2/target-client2-role的角色范围映射
  • 客户端请求 程序-客户端 范围为 default-scope1,它添加了默认客户端范围,范围 optional-scope2 添加为可选客户端范围
  • 经过身份验证的用户,该用户是 target-client1-roletarget-client2-role的成员

上面的设置意味着,使用范围 default-scope1 会将 audience target-client1 添加到令牌中,并使用 optional-scope2 将添加 audience target-client2。这是因为受众 文档 所描述的受众解析。

12.1.4.1.1. 示例 1

令牌交换请求通过 scope=optional-scope2 和没有 audience 参数发送:

不对听众进行过滤。范围和受众将作为任何其他授予的情况解决,如 客户端范围和 Audience 文档部分中所述。响应令牌将与此类似(对于此示例为 brevity 省略的声明并不有帮助):

{
  "azp": "requester-client",
  "scope": "default-scope1 optional-scope2",
  "aud": [ "target-client1", "target-client2" ],
  "resource_access": {
	"target-client1": {
  	  "roles": [ "target-client1-role" ]
	},
	"target-client2": {
  	  "roles": [ "target-client2-role" ]
	}
  },
  ...
}
12.1.4.1.2. 示例 2

使用 scope=optional-scope2audience=target-client2发送的令牌交换请求

与前面的示例相同,但 target-client1 audience 和 client 角色过滤自 audience 参数,但仅包含此 target-client2 客户端。客户端范围 default-scope1 也会被过滤,因为它包含一些客户端角色,但没有请求的 audience 客户端 target-client2 的客户端角色。因此,令牌将类似如下:

{
  "azp": "requester-client",
  "scope": "optional-scope2",
  "aud": [ "target-client2" ],
  "resource_access": {
    "target-client2": {
      "roles": [ "target-client2-role" ]
    }
  },
  ...
}
12.1.4.1.3. 示例 3

令牌交换请求通过 scope=optional-scope2audience=target-client2&audience=target-client3发送

target-client3 不是令牌 audience 的一部分,因为用户没有任何角色。因此,由于某些请求的受众不可用,请求将被拒绝。

注意

如令牌交换规范中所述,最好尽可能减少令牌范围,并且仅使用所需的受众。理想情况下,使用单个受众。此策略提高了允许请求的概率。

注意

如果您的部署具有多个不同范围和受众的复杂部署,则以适当的方式对部署进行建模可能具有挑战性。考虑使用 Client range evaluate 选项卡 来测试令牌是否为给定用户以及给定范围和 audiences 集合的预期。

12.1.5. 令牌交换 - 其他详情

这些额外的点明确令牌交换的行为。

  • 公共客户端不支持发送令牌交换请求。令牌交换 V1 包括对公共客户端的有限支持,允许公共客户端将令牌交换到自己较少的范围。此用例可通过刷新令牌授权来替代。
  • 发送到令牌交换端点的 subject_token 必须将请求者客户端设置为 aud 声明中的 audience。否则,请求将被拒绝。唯一的例外是,如果客户端交换自己的令牌,即客户端向其发出了自己的令牌。交换自身对于减少令牌范围/范围可能很有用,或者过滤不需要的令牌受众等。
  • consents - 如果请求者客户端启用了 Consent 需要,则只有在用户已授予所有请求范围的同意时才允许令牌交换
  • 标准令牌交换不需要 细粒度 admin 权限(FGAP)。我们计划未来与 FGAP 集成,但该集成可能适用于所有补贴。它不仅特定于令牌交换,因为它在令牌交换 V1 中。
  • 可以将令牌交换与客户端 策略 集成。此集成可用于解决某些用例。例如,如果客户端 请求者-客户端通过 scope=some-confidential- scope 发送请求,则请考虑拒绝令牌交换请求的用例。在本例中,创建客户端策略条件以及 client-scopegrant-typeclient-roles 的组合条件会很有用。
  • 只有在客户端在 Standard Token Exchange 中将 switch Allow refresh token 设置为非非值时,才允许 请求刷新令牌。交换机包括在 OpenID Connect Compatibility Modes 部分的 OIDC 客户端 Advanced 标签页的 Admin Console 中。交换机的另一个可用值为 Same 会话,这意味着只有在刷新令牌可以使用与主题令牌相同的用户会话时,才允许刷新令牌。如果该主题令牌来自 Transient 会话 或从 离线会话中,则不允许请求刷新令牌。同样,不允许请求离线令牌(使用 scope=offline_access)。

图 12.2. 在令牌交换中启用刷新令牌

在令牌交换中启用刷新令牌
  • 令牌交换永远不会 创建新用户会话。如果 requested_token_type 是刷新令牌,它最终可能会在请求者客户端的用户会话中创建一个新的客户端会话(如果还没有创建客户端会话)。
  • Red Hat build of Keycloak Token Exchange 尚不支持 resource 参数。
  • 令牌交换规范提到了 模仿和委派 的概念。红帽构建的 Keycloak 支持模拟用例,但尚不支持委派用例。
12.1.5.1. 吊销

假设向客户端 initial-client 发出了一个主题令牌 access-token1,以下与令牌撤销相关的注意事项:

  • 在这种情况下,当将 access-token1 与客户端请求er-client 的 access-token2 交换时,access-token1 的撤销不会撤销 access-token2支持用于访问令牌的"撤销链"意味着相当大的开销。因此,管理员必须确保访问令牌已短,并在一段时间后自动撤销。
  • 对于这种情况,当将 access-token1 交换为客户端 requester-clientrefresh-token2 时,我们会尝试支持撤销链。这意味着:

    • 撤销 access-token1 将撤销也会撤销 refresh-token2。此外,这将从用户会话中删除客户端 请求者- 客户端的客户端会话,因此此用户会话中 请求者-客户端 的所有刷新令牌都将有效撤销
    • 如果 refresh-token2 及其相关访问令牌用于进一步与不同的客户端进行令牌交换,则 access-token1 的撤销也会撤销这些后续令牌交换。换句话说,将撤销交换令牌的整个"链"。
    • 请注意,当调用撤销端点时,访问令牌应该有效。如果您在原始 access-token1 过期时没有有效的访问令牌,您可能会在同一用户会话中对同一客户端发出的另一个访问令牌。应撤销来自"chain"的交换令牌,如 refresh-token2 和其他令牌。

12.1.6. 标准令牌交换和旧令牌交换的比较

虽然前面部分详细介绍了标准和旧令牌交换,但以下是比较两个令牌交换方法的整体摘要。

Expand
功能标准令牌交换 V2传统令牌交换 V1

内部令牌交换

支持。根据 rfc8693 实施

预览支持.rfc8693 的松散实施。建议使用 V2 替代

allowed subject_token_type

仅限访问令牌类型

仅限内部令牌类型,JWT 用于外部内部场景

Allowed requested_token_type

访问令牌(默认)、刷新令牌、ID 令牌

访问令牌、刷新令牌(默认)、SAML2 断言

scope 参数的行为

与其他补贴一致。scope 参数意味着请求客户端的可选范围,后者发送令牌交换请求

scope 参数基于由 audience 参数指定的 "target" 客户端的范围。只支持 Downscoping

audience 参数的行为

根据规格支持更多值。它可用于缩小可用受众的范围,仅保留请求的受众。根据所需的目标受众有效地缩减令牌

支持单个受众价值.audience 参数有效地向客户端发出的令牌,并使用该客户端的范围

公共客户端

不可用。由 V1 实施的 Downscoping 可以被刷新令牌授权替代

仅适用于交换客户端本身的令牌。有效地缩减支持

同意

只要已授予用户同意,则允许具有 Consent 的客户端

不允许具有 Consent 所需的客户端

授权

验证请求者客户端必须在 subject_token 的 audience 中。与客户端策略集成。无细粒度 admin 权限

基于精细的 admin 权限版本 1

吊销链

不适用于访问令牌。可用于刷新令牌

无法访问或刷新令牌

每个 rfc8693 的委派

尚不支持

不支持

每个 rfc8693 的资源参数

尚不支持

不支持

联邦令牌交换

尚未实施

作为技术预览实现

主题模拟(包括直接利用的模拟)

尚未实施

作为技术预览实现

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

它接受表单参数(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" : "...."
 }

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

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

{
   "error" : "...."
   "error_description" : "...."
}

根据交换类型,可能会返回其他错误声明。例如,如果用户没有到身份提供程序的链接,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

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

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

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

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

{
   "access_token" : "....",
   "refresh_token" : "....",
   "expires_in" : 3600
}

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

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://...."
}

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

{
   "error" : "....",
   "error_description" : "..."
   "account-link-url" : "https://...."
}

错误 声明将是 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

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

{
   "access_token" : "....",
   "refresh_token" : "....",
   "expires_in" : 3600
}

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

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

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

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

12.2.8. 交换漏洞

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

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

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

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

第 13 章 实施规格

红帽构建的 Keycloak 实施的规格和标准列表。

本章介绍了红帽构建的 Keycloak 当前实施的规格和标准列表。标准在不同的部分中分隔,各自会显示一个表,包含以下四列:

  • 规格 :红帽构建的 Keycloak 实施的标准或规格。
  • Status: 红帽构建的 Keycloak (支持的、预览、实验性、…​)中实现的当前状态。如需更多信息,请参阅 {links_server_features_name}
  • 一致性 :实施保证的保证。

    • 认证(版本) :规范提供红帽构建的 Keycloak 定期和每个新版本执行的一致性测试。括号中的版本是该机构认证的 Keycloak 的最后一个版本。
    • 传递:红帽构建的 Keycloak 通过的权威提供一致性测试,但还没有认证版本。
    • 部分 :有一致性测试,但红帽构建的 Keycloak 尚未完全传递。
    • 如果此列为空,则红帽构建的 Keycloak 不会为 spec 传递任何外部一致性测试。仅执行常见的项目集成测试。权利不提供一致性测试套件,或红帽构建的 Keycloak 不适用于传递它们。
  • 注释 :包含实施或状态详情的通用列。例如,尚未覆盖或特定行为的部分在 spec 中。

13.1. OpenID Connect

Expand
规格Status一致性注释

OpenID Connect Core

支持

认证(18.0.0)

 

OpenID Connect Discovery

支持

认证(18.0.0)

 

OpenID Connect Dynamic Client Registration

支持

认证(18.0.0)

 

OpenID Connect 会话管理

支持

认证(18.0.0)

 

OpenID Connect RP-Initiated Logout

支持

认证(18.0.0)

 

OpenID Connect Back-Channel Logout

支持

认证(18.0.0)

 

OpenID Connect Front-Channel Logout

支持

认证(18.0.0)

 

OpenID Connect Client-Initiated Backchannel Authentication Flow

支持

认证(18.0.0)

 

OAuth 2.0 多响应类型编码实践

支持

认证(18.0.0)

 

OAuth 2.0 表后响应模式

支持

认证(18.0.0)

 

通过 OpenID Connect 1.0 启动用户注册

支持

  

OpenID for Verifiable Credential Issuance (OID4VCI)

实验性

  

13.2. OAuth

13.3. 财务级 API (FAPI)

13.4. 安全断言标记语言(SAML)

Expand
规格Status一致性注释

安全断言标记语言(SAML) v2.0

支持

 

这个标准涵盖了多个绑定和上下文。Red Hat build of Keycloak 实施一整范围,但缺少一些部分以确保。

13.5. 用户管理的访问(UMA)

13.6. JSON Web

13.7. misc

Expand
规格Status一致性注释

Cryptographic Modules (FIPS 140-2)的安全要求

支持

已认证

Red Hat build of Keycloak 使用 Bouncy Castle (BC) FIPS 库来提供 FIPS 140-2。BC 确实是认证的 FIPS 140-3 实施,但也需要一个经过认证的堆栈(操作系统和 Java 虚拟机)。如需更多信息,请参阅 {links_server_fips_name}

Web 身份验证:用于访问公钥凭证级别 2 的 API

支持

 

此规格具有一致性测试,但红帽构建的 Keycloak 没有使用它们。Red Hat build of Keycloak 充当此规范的 WebAuthn Relying Party (RP)。

第 14 章 Red Hat build of Keycloak admin client

使用红帽构建的 Keycloak admin 客户端来访问红帽构建的 Keycloak Admin REST API。

Red Hat build of Keycloak admin client 是一个 Java 库,可帮助访问和使用 Red Hat build of Keycloak Admin REST API。该程序库在运行时需要 Java 11 或更高版本(RESTEasy 依赖项强制实施这个要求)。要从应用程序中使用它,请添加对 keycloak-admin-client 库的依赖项。例如,使用 Maven:

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-admin-client</artifactId>
    <version>999.0.0-SNAPSHOT</version>
</dependency>

以下示例演示了如何使用 Java 客户端库来获取 master 域的详情:

import org.keycloak.admin.client.Keycloak;
import org.keycloak.representations.idm.RealmRepresentation;
...

Keycloak keycloak = Keycloak.getInstance(
    "http://localhost:8080",
    "master",
    "admin",
    "password",
    "admin-cli");
RealmRepresentation realm = keycloak.realm("master").toRepresentation();

admin 客户端的完整 Javadoc 位于 API 文档

14.1. 与红帽构建的 Keycloak 服务器兼容

Red Hat build of Keycloak admin 客户端旨在与 Red Hat build of Keycloak 服务器的多个版本一起工作。管理客户端可能会支持较早发布的红帽构建的 Keycloak 服务器版本,它比客户端和较早版本的 Keycloak 服务器发布。因此,底层"弹性"类的 Java 字段代表请求/响应正文的 JSON 属性(如上一节中显示的 RealmRepresentation 类)可能并不完全相同。

为了避免兼容性问题,请确保在覆盖下的 admin 客户端使用 com.fasterxml.jackson.databind.ObjectMapper 类,使用这两个属性初始化:

objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

如果您使用上述管理客户端创建的基本方法,则默认添加这些属性,因为 admin 客户端默认使用 org.keycloak.admin.client.JacksonProvider 类用于创建 ObjectMapper,这将自动添加这些属性。但是,如果您在创建 Keycloak 对象时注入自己的 customJacksonProvider,请确保在希望避免兼容性问题时使用上述属性初始化对象映射。

例如,请考虑 admin 客户端以您自己 MyCustomJacksonProvider 类进行实例化的情况,如下所示:

Keycloak.getInstance(
                "http://localhost:8080",
                "master",
                "admin",
                "admin",
                "admin-cli",
                null,
                null,
                new MyCustomJacksonProvider()
        );

在这种情况下,请确保您的类 MyCustomJacksonProvider 从类 org.keycloak.admin.client.JacksonProvider 扩展,或者确保以上述方式手动配置 ObjectMapper。在使用 KeycloakBuilder 创建管理客户端并且手动注入和创建 RestEasy 客户端时,应谨慎操作。

第 15 章 Red Hat build of Keycloak 授权客户端

使用红帽构建的 Keycloak authz 客户端管理和检查权限。

根据您的要求,资源服务器应能够远程管理资源,甚至以编程方式检查权限。如果使用 Java,您可以使用授权客户端 API 访问红帽构建的 Keycloak 授权服务。

它是用于访问服务器提供的不同端点的资源服务器,如 Token Endpoint、Resource 和 Permission 管理端点。

15.1. Maven 依赖项

<dependencies>
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-authz-client</artifactId>
        <version>999.0.0-SNAPSHOT</version>
    </dependency>
</dependencies>

15.2. Configuration

客户端配置在 keycloak.json 文件中定义,如下所示:

{
  "realm": "hello-world-authz",
  "auth-server-url" : "http://localhost:8080",
  "resource" : "hello-world-authz-service",
  "credentials": {
    "secret": "secret"
  }
}
  • realm (必需)

    域的名称。

  • auth-server-url (必需)

    红帽构建的 Keycloak 服务器的基本 URL。所有其他红帽构建的 Keycloak 页面和 REST 服务端点都源自此目的。它通常采用 https://host:port 的形式。

  • resource (必需)

    应用程序的 client-id。每个应用程序都有一个客户端 ID,用于标识应用程序。

  • credentials (必需)

    指定应用程序的凭证。这是一个对象表示法,其中键是凭证类型,值是凭证类型的值。详情位于 专用部分

配置文件通常位于应用程序的 classpath 中,客户端要尝试从中查找 keycloak.json 文件的默认位置。

15.3. 创建授权客户端

考虑在 classpath 中有一个 keycloak.json 文件,您可以创建一个新的 AuthzClient 实例,如下所示:

// create a new instance based on the configuration defined in a keycloak.json located in your classpath
AuthzClient authzClient = AuthzClient.create();

15.4. 获取用户权利

以下是如何获取用户权利的示例:

// create a new instance based on the configuration defined in keycloak.json
AuthzClient authzClient = AuthzClient.create();

// create an authorization request
AuthorizationRequest request = new AuthorizationRequest();

// send the entitlement request to the server in order to
// obtain an RPT with all permissions granted to the user
AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize(request);
String rpt = response.getToken();

System.out.println("You got an RPT: " + rpt);

// now you can use the RPT to access protected resources on the resource server

以下是如何获取一个或多个资源集合的用户权利的示例:

// create a new instance based on the configuration defined in keycloak.json
AuthzClient authzClient = AuthzClient.create();

// create an authorization request
AuthorizationRequest request = new AuthorizationRequest();

// add permissions to the request based on the resources and scopes you want to check access
request.addPermission("Default Resource");

// send the entitlement request to the server in order to
// obtain an RPT with permissions for a single resource
AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize(request);
String rpt = response.getToken();

System.out.println("You got an RPT: " + rpt);

// now you can use the RPT to access protected resources on the resource server

15.5. 使用保护 API 创建资源

// create a new instance based on the configuration defined in keycloak.json
AuthzClient authzClient = AuthzClient.create();

// create a new resource representation with the information we want
ResourceRepresentation newResource = new ResourceRepresentation();

newResource.setName("New Resource");
newResource.setType("urn:hello-world-authz:resources:example");

newResource.addScope(new ScopeRepresentation("urn:hello-world-authz:scopes:view"));

ProtectedResource resourceClient = authzClient.protection().resource();
ResourceRepresentation existingResource = resourceClient.findByName(newResource.getName());

if (existingResource != null) {
    resourceClient.delete(existingResource.getId());
}

// create the resource on the server
ResourceRepresentation response = resourceClient.create(newResource);
String resourceId = response.getId();

// query the resource using its newly generated id
ResourceRepresentation resource = resourceClient.findById(resourceId);

System.out.println(resource);

15.6. 内省 RPT

// create a new instance based on the configuration defined in keycloak.json
AuthzClient authzClient = AuthzClient.create();

// send the authorization request to the server in order to
// obtain an RPT with all permissions granted to the user
AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize();
String rpt = response.getToken();

// introspect the token
TokenIntrospectionResponse requestingPartyToken = authzClient.protection().introspectRequestingPartyToken(rpt);

System.out.println("Token status is: " + requestingPartyToken.getActive());
System.out.println("Permissions granted by the server: ");

for (Permission granted : requestingPartyToken.getPermissions()) {
    System.out.println(granted);
}

15.7. 客户端身份验证

当授权客户端需要发送后端请求时,它需要针对红帽构建的 Keycloak 服务器进行身份验证。默认情况下,可以通过三种方式对客户端进行身份验证:客户端 ID 和客户端机密、使用签名 JWT 的客户端身份验证,或者使用客户端机密通过签名 JWT 进行客户端身份验证。

15.7.1. 客户端 ID 和客户端 Secret

这是 OAuth2 规范中描述的传统方法。客户端有一个 secret,需要对客户端和红帽构建的 Keycloak 服务器都已知。您可以在红帽构建的 Keycloak 管理控制台中为特定客户端生成 secret,然后将此 secret 粘贴到应用程序端的 keycloak.json 文件中:

"credentials": {
    "secret": "19666a4f-32dd-4049-b082-684c74115f28"
}

15.7.2. 使用 Signed JWT 进行客户端身份验证

这基于 RFC7523 规格。它以这种方式工作:

  • 客户端必须具有私钥和证书。对于授权客户端,这可通过传统的 密钥存储文件 提供,该文件可以在客户端应用的 classpath 上或文件系统上的某个位置使用。
  • 在身份验证过程中,客户端会生成 JWT 令牌并使用其私钥进行签名,并将其发送到 client_assertion 参数中特定请求中的 Keycloak 的红帽构建。
  • 红帽构建的 Keycloak 必须有客户端的公钥或证书,以便它可以验证 JWT 上的签名。在 Red Hat build of Keycloak 中,您可以为客户端配置客户端凭证。首先,您可以选择 Signed JWT 作为在管理控制台 选项卡中 验证客户端的方法。然后您可以在 Keys 选项卡中选择这些方法之一:

    • 配置红帽构建的 Keycloak 可下载客户端的公钥的 JWKS URL。这个选项是最灵活的,因为客户端可以随时轮转其密钥,红帽构建的 Keycloak 始终根据需要下载新密钥,而无需更改配置。换句话说,当看到由未知 kid (Key ID)签名的令牌时,Red Hat build of Keycloak 会下载新密钥。但是,您将需要在某种地方公开 JWKS 格式的公钥,以便供服务器使用。
    • 上传客户端的公钥或证书,可以是 PEM 格式,可以是 JWK 格式,或从密钥存储上传。使用此选项时,公钥被硬编码,必须在客户端生成新的密钥对时更改。如果您没有自己的密钥存储可用,则甚至可以从红帽构建的 Keycloak 管理控制台生成自己的密钥存储。此选项是使用授权客户端的最简单。

要使用此方法设置,您需要在 keycloak.json 文件中编写类似如下的代码:

"credentials": {
  "jwt": {
    "client-keystore-file": "classpath:keystore-client.jks",
    "client-keystore-type": "JKS",
    "client-keystore-password": "storepass",
    "client-key-password": "keypass",
    "client-key-alias": "clientkey",
    "token-expiration": 10
  }
}

使用此配置时,密钥存储文件 keystore-client.jks 必须在应用的类路径中使用授权客户端。如果不使用前缀 classpath: 您可以指向运行客户端应用程序的文件系统上的任何文件。

这与带有 Signed JWT 的客户端身份验证相同,但使用客户端 secret 而不是私钥和证书。

客户端有一个 secret,需要使用授权客户端和红帽构建的 Keycloak 服务器对应用程序进行已知。您可以选择 Signed JWT with Client Secret 作为在 Admin Console 的 Credentials 选项卡中验证客户端的方法,然后将此 secret 粘贴到应用程序端的 keycloak.json 文件中:

"credentials": {
  "secret-jwt": {
    "secret": "19666a4f-32dd-4049-b082-684c74115f28",
    "algorithm": "HS512"
  }
}

"algorithm"字段使用 Client Secret 指定 Signed JWT 的算法。它需要是以下值之一:HS256、HS384 和 HS512。详情请参阅 JSON Web 算法(JWA)

此 "algorithm" 字段是可选的;如果 keycloak.json 文件中不存在 "algorithm" 字段,则会自动应用 HS256。

15.7.4. 添加您自己的客户端验证方法

您还可以添加自己的客户端验证方法。您需要实施客户端和服务器端提供商。如需了解更多详细信息,请参阅 服务器 开发人员指南中的 身份验证 SPI 部分。

第 16 章 Red Hat build of Keycloak policy enforcer

在 Java 应用程序中使用红帽构建的 Keycloak 策略强制器。

策略强制点(PEP)是一种设计模式,因此您可以以不同的方式实施它。Red Hat build of Keycloak 提供了为不同平台、环境和编程语言实施 PEP 所需的所有方法。红帽构建的 Keycloak 授权服务提供了一个 RESTful API,并利用 OAuth2 授权功能来使用集中式授权服务器进行精细授权。

PEP 概述

PEP 负责实施红帽构建的 Keycloak 服务器的访问决策,通过评估与保护资源关联的策略来实施这些决策。它充当应用程序中过滤或拦截器,以检查对受保护的资源的特定请求是否可以根据这些决策授予的权限实现。

Red Hat build of Keycloak 提供内置支持,以便 红帽构建 Keycloak 策略 Enforcer 到具有内置支持来保护与 JakartaEE 兼容框架和 Web 容器的 Java 应用程序。如果使用 Maven,您应该为项目配置以下依赖项:

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-policy-enforcer</artifactId>
    <version>999.0.0-SNAPSHOT</version>
</dependency>

当您启用发送到应用程序的策略强制器时,会截获访问受保护的资源,具体取决于红帽构建的 Keycloak 权限到发出请求的身份。

策略强制使用红帽构建的 Keycloak 管理控制台与应用程序的路径以及您为资源服务器创建的资源相关联。https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.4/html-single/authorization_services_guide/#_resource_overview默认情况下,当您创建资源服务器时,Red Hat build of Keycloak 会为资源服务器创建一个 默认配置,以便您可以快速启用策略执行。

16.1. 配置

策略强制配置使用 JSON 格式,如果您想根据资源服务器可用的资源自动解析受保护的路径,则不需要设置任何时间。

如果要手动定义受保护的资源,您可以使用稍微详细的格式:

{
  "enforcement-mode" : "ENFORCING",
  "paths": [
    {
      "path" : "/users/*",
      "methods" : [
        {
          "method": "GET",
          "scopes" : ["urn:app.com:scopes:view"]
        },
        {
          "method": "POST",
          "scopes" : ["urn:app.com:scopes:create"]
        }
      ]
    }
  ]
}

以下是每个配置选项的描述:

  • enforcement-mode

    指定如何强制实施策略。

    • ENFORCING

      (默认模式)默认拒绝请求,即使没有与给定资源关联的策略。

    • PERMISSIVE

      即使没有策略与给定资源关联,也允许请求。

    • DISABLED

      完全禁用策略评估并允许访问任何资源。当 enforcement-modeDISABLED 时,应用程序仍然能够通过 授权上下文获取红帽构建的 Keycloak 授予的所有权限

  • on-deny-redirect-to

    定义一个 URL,在从服务器获取"访问被拒绝"消息时重定向客户端请求。默认情况下,适配器会以 403 HTTP 状态代码进行响应。

  • path-cache

    定义策略如何跟踪应用程序中的路径与 Red Hat build of Keycloak 中定义的资源之间的关联。需要缓存以避免通过路径和保护资源之间的缓存关联来避免对 Red Hat build of Keycloak 服务器的不必要的请求。

    • lifespan

      定义条目应过期的时间(以毫秒为单位)。如果没有提供,则默认值为 30000。等于 0 的值可以设置为完全禁用缓存。这样设置一个等于 -1 的值来禁用缓存的过期。

    • max-entries

      定义应在缓存中保留的条目的限值。如果没有提供,则默认值为 1000

  • paths

    指定保护的路径。此配置是可选的。如果没有定义,策略通过获取您在红帽构建的 Keycloak 中定义的资源来发现所有路径,其中这些资源使用代表应用程序中某些路径的 URIS 定义。

    • name

      要与给定路径关联的服务器上的资源名称。与 路径 结合使用时,策略强制执行器会忽略资源的 URIS 属性,并使用您提供的路径。

    • path

      (必需)相对于应用程序上下文路径的 URI。如果指定了这个选项,策略会执行服务器查询具有相同值的 URI 的资源。目前,支持非常基本的路径匹配逻辑。有效路径示例包括:

      • 通配符: configured
      • 后缀: configured.html
      • 子路径: /path configured
      • 路径参数: /resource/{id}
      • 完全匹配:/resource
      • patterns: /{version}/resource, /api/{version}/resource, /api/{version}/resource configured
    • methods

      HTTP 方法(如 GET、POST、PATCH)来保护以及它们如何与服务器中给定资源的范围关联。

      • 方法

        HTTP 方法的名称。

      • scopes

        与方法关联的范围的字符串数组。当您将范围与特定方法关联时,尝试访问受保护的资源(或路径)的客户端必须提供 RPT,它将向列表中指定的所有范围授予权限。例如,如果您使用范围 创建 定义了方法 POST,RPT 必须包含在执行 POST 到路径时授予 创建 范围的访问权限。

      • scopes-enforcement-mode

        引用与方法关联的范围的强制模式的字符串。值可以是 ALLANY。如果为 ALL,则必须授予所有定义的范围才能使用该方法访问资源。如果为 ANY,则应授予至少一个范围以获取使用该方法的资源的访问权限。默认情况下,强制模式设置为 ALL

    • enforcement-mode

      指定如何强制实施策略。

      • ENFORCING

        (默认模式)默认拒绝请求,即使没有与给定资源关联的策略。

      • DISABLED
    • claim-information-point

      定义一组必须解析并推送到红帽构建的 Keycloak 服务器的一个或多个声明,以便这些声明可供策略使用。如需了解更多详细信息,请参阅 申索信息点

  • lazy-load-paths

    指定适配器应如何获取与应用程序中路径关联的资源的服务器。如果为 true,策略强制器将通过请求的路径相应地获取资源。当您不希望在部署过程中从服务器获取所有资源(如果您未提供 路径),或者您只定义了一组子路径并希望按需获取 其他路径 时,此配置特别有用。

  • http-method-as-scope

    指定范围应如何映射到 HTTP 方法。如果设置为 true,策略执行者将使用当前请求中的 HTTP 方法来检查是否应授予访问权限。启用后,请确保红帽构建的 Keycloak 中的资源与代表您要保护的每个 HTTP 方法的范围关联。

  • claim-information-point

    定义一组必须解析并推送到红帽构建的 Keycloak 服务器的一个或多个 全局 声明,以便这些声明可供策略使用。如需了解更多详细信息,请参阅 申索信息点

16.2. 声明信息点

申索信息点(CIP)负责解析声明并将这些声明推送到红帽构建的 Keycloak 服务器,以提供有关策略访问上下文的更多信息。它们可以定义为 policy-enforcer 的配置选项,以便解析来自不同源的声明,例如:

  • HTTP 请求(参数、标头、正文等)
  • 外部 HTTP 服务
  • 配置中定义的静态值
  • 通过实施 Claim Information Provider SPI 来任何其他源

当将声明推送到红帽构建的 Keycloak 服务器时,策略不仅可以考虑用户是谁,还可以根据具体情况、何时、何时以及给定事务使用上下文和内容来考虑基础决策。它都是基于上下文的授权以及如何使用运行时信息,以支持细粒度授权决策。

16.2.1. 从 HTTP 请求获取信息

下面是几个示例,演示了如何从 HTTP 请求中提取声明:

keycloak.json

{
  "paths": [
    {
      "path": "/protected/resource",
      "claim-information-point": {
        "claims": {
          "claim-from-request-parameter": "{request.parameter['a']}",
          "claim-from-header": "{request.header['b']}",
          "claim-from-cookie": "{request.cookie['c']}",
          "claim-from-remoteAddr": "{request.remoteAddr}",
          "claim-from-method": "{request.method}",
          "claim-from-uri": "{request.uri}",
          "claim-from-relativePath": "{request.relativePath}",
          "claim-from-secure": "{request.secure}",
          "claim-from-json-body-object": "{request.body['/a/b/c']}",
          "claim-from-json-body-array": "{request.body['/d/1']}",
          "claim-from-body": "{request.body}",
          "claim-from-static-value": "static value",
          "claim-from-multiple-static-value": ["static", "value"],
          "param-replace-multiple-placeholder": "Test {keycloak.access_token['/custom_claim/0']} and {request.parameter['a']}"
        }
      }
    }
  ]
}

16.2.2. 从外部 HTTP 服务获取信息

下面是几个示例,演示了如何从外部 HTTP 服务提取声明:

keycloak.json

{
  "paths": [
    {
      "path": "/protected/resource",
      "claim-information-point": {
        "http": {
          "claims": {
            "claim-a": "/a",
            "claim-d": "/d",
            "claim-d0": "/d/0",
            "claim-d-all": [
              "/d/0",
              "/d/1"
            ]
          },
          "url": "http://mycompany/claim-provider",
          "method": "POST",
          "headers": {
            "Content-Type": "application/x-www-form-urlencoded",
            "header-b": [
              "header-b-value1",
              "header-b-value2"
            ],
            "Authorization": "Bearer {keycloak.access_token}"
          },
          "parameters": {
            "param-a": [
              "param-a-value1",
              "param-a-value2"
            ],
            "param-subject": "{keycloak.access_token['/sub']}",
            "param-user-name": "{keycloak.access_token['/preferred_username']}",
            "param-other-claims": "{keycloak.access_token['/custom_claim']}"
          }
        }
      }
    }
  ]
}

16.2.3. 静态声明

keycloak.json

{
  "paths": [
    {
      "path": "/protected/resource",
      "claim-information-point": {
        "claims": {
          "claim-from-static-value": "static value",
          "claim-from-multiple-static-value": ["static", "value"]
        }
      }
    }
  ]
}

16.2.4. 声明信息提供商 SPI

开发人员可以使用 Claim Information Provider SPI 来支持不同的声明信息点(如果内置的供应商都不够)来满足自己的要求。

例如,要实施新的 CIP 提供程序,您需要在应用程序的 classpath 中实施 org.keycloak.adapters.authorization.ClaimInformationPointProviderFactoryClaimInformationPointProvider,并提供文件 META-INF/services/org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory

org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory 示例:

public class MyClaimInformationPointProviderFactory implements ClaimInformationPointProviderFactory<MyClaimInformationPointProvider> {

    @Override
    public String getName() {
        return "my-claims";
    }

    @Override
    public void init(PolicyEnforcer policyEnforcer) {

    }

    @Override
    public MyClaimInformationPointProvider create(Map<String, Object> config) {
        return new MyClaimInformationPointProvider(config);
    }
}

每个 CIP 供应商必须与名称关联,如 MyClaimInformationPointProviderFactory.getName 方法中定义的名称。该名称将用于将配置从 policy-enforcer 配置中的 claim-information-point 部分中映射到实施。

在处理请求时,策略 enforcer 将调用 MyClaimInformationPointProviderFactory.create 方法,以获取 MyClaimInformationPointProvider 实例。调用时,为此特定 CIP 提供程序(通过 claim-information-point)定义的任何配置都会作为映射传递。

ClaimInformationPointProvider 示例:

public class MyClaimInformationPointProvider implements ClaimInformationPointProvider {

    private final Map<String, Object> config;

    public MyClaimInformationPointProvider(Map<String, Object> config) {
        this.config = config;
    }

    @Override
    public Map<String, List<String>> resolve(HttpFacade httpFacade) {
        Map<String, List<String>> claims = new HashMap<>();

        // put whatever claim you want into the map

        return claims;
    }
}

16.3. 获取授权上下文

启用策略强制时,可以通过 org.keycloak.AuthorizationContext 从服务器获取的权限。此类提供了多种方法,您可以用来获取权限,并确定是否为特定资源或范围授予了权限。

在 Servlet 容器中获取授权上下文

HttpServletRequest request = // obtain javax.servlet.http.HttpServletRequest
AuthorizationContext authzContext = (AuthorizationContext) request.getAttribute(AuthorizationContext.class.getName());
注意

授权上下文可帮助您更好地控制服务器制定和返回的决策。例如,您可以根据与资源或范围关联的权限,使用它来构建动态菜单,其中项目会被隐藏或显示。

if (authzContext.hasResourcePermission("Project Resource")) {
    // user can access the Project Resource
}

if (authzContext.hasResourcePermission("Admin Resource")) {
    // user can access administration resources
}

if (authzContext.hasScopePermission("urn:project.com:project:create")) {
    // user can create new projects
}

AuthorizationContext 代表红帽构建的 Keycloak 授权服务的主要功能之一。在上面的示例中,您可以看到受保护的资源不与监管这些资源的策略直接关联。

考虑使用基于角色的访问控制(RBAC)的一些类似代码:

if (User.hasRole('user')) {
    // user can access the Project Resource
}

if (User.hasRole('admin')) {
    // user can access administration resources
}

if (User.hasRole('project-manager')) {
    // user can create new projects
}

虽然这两个示例都满足了同样的要求,但它们以不同的方式实现。在 RBAC 中,角色仅 隐式 定义其资源的访问权限。通过 Red Hat build of Keycloak,您可以获得创建更易于管理的代码的功能,这些代码直接侧重于资源,无论您使用的是 RBAC、属性的访问控制(ABAC)还是任何其他 BAC 变体。您有给定资源或范围的权限,或者您没有该权限。

现在,假设您的安全要求已更改,并且除了项目管理器外,PMO 也可以创建新项目。

安全要求变化,但使用红帽构建的 Keycloak 无需更改应用程序代码来满足新的要求。应用基于资源和范围标识符后,您只需要更改与授权服务器中特定资源关联的权限或策略的配置。在本例中,与项目资源关联的权限和策略,或范围 urn:project.com:project:create 将被更改。

AuthorizationContext 也可用于获取配置给应用程序的 红帽构建的 Keycloak 授权客户端 的引用:

ClientAuthorizationContext clientContext = ClientAuthorizationContext.class.cast(authzContext);
AuthzClient authzClient = clientContext.getClient();

在某些情况下,由策略强制程序保护的资源服务器需要访问授权服务器提供的 API。通过实践中的 AuthzClient 实例,资源服务器可以与服务器交互,以创建资源或以编程方式检查特定权限。

16.5. 配置 TLS/HTTPS

当服务器使用 HTTPS 时,请确保您的策略 enforcer 被配置为如下:

{
  "truststore": "path_to_your_trust_store",
  "truststore-password": "trust_store_password"
}

以上配置启用了 TLS/HTTPS 到授权客户端,从而可以使用 HTTPS 方案远程访问 Keycloak 服务器的红帽构建。

注意

强烈建议您在访问红帽构建的 Keycloak 服务器端点时启用 TLS/HTTPS。

如何升级红帽构建的 Keycloak 客户端库。

客户端库是这些工件:

  • Java 管理客户端 - Maven 工件 org.keycloak:keycloak-admin-client
  • Java 授权客户端 - Maven 工件 org.keycloak:keycloak-authz-client
  • Java 策略强制 - Maven 工件 org.keycloak:keycloak-policy-enforcer
  • 上面其他客户端库使用的 Java 通用类 - Maven 工件 org.keycloak:keycloak-client-common-synced

客户端库支持所有支持的 Keycloak 服务器版本。通过更多服务器版本支持客户端库可以更轻松地更新,因此当更新应用程序的客户端库时,您可能不需要同时更新服务器。

客户端库甚至可以和红帽构建的 Keycloak 服务器的旧版本一起工作,但无法保证并正式支持。

可能需要查阅 Java admin-client 等客户端库的 javadoc,以查看红帽构建的 Keycloak 服务器版本支持哪些端点和参数。对于 admin 客户端,请参阅 Red Hat build of Keycloak admin client 章节中的 "Compatibility with Red Hat build of Keycloak server"。

法律通告

Copyright © 2025 Red Hat, Inc.
根据 Apache 许可证(版本 2.0)授权(License");除非遵守许可证,您可能不能使用此文件。您可以在以下位置获取许可证副本
除非适用法律或同意编写,许可证下的软件将由"AS IS"BASIS 分发,WITHOUT WARRANTIES 或 CONDITIONS OF ANY KIND,可以是表达或表示的。有关许可证下的权限和限制的具体语言,请参阅许可证。
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

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

让开源更具包容性

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

關於紅帽

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

Theme

© 2026 Red Hat
返回顶部