OpenID Connect (OIDC)客户端和令牌传播
摘要
第 1 章 OpenID Connect (OIDC)和 OAuth2 客户端和过滤器 复制链接链接已复制到粘贴板!
您可以使用 Quarkus 扩展进行 OpenID Connect 和 OAuth 2.0 访问令牌管理,专注于获取、刷新和传播令牌。
这包括以下内容:
-
使用
quarkus-oidc-client,quarkus-rest-client-oidc-filter和quarkus-resteasy-client-oidc-filter扩展从 OpenID Connect 和 OAuth 2.0 兼容授权服务器(如 Keycloak )获取和刷新访问令牌。 -
使用
quarkus-rest-client-oidc-token-propagation和quarkus-resteasy-client-oidc-token-propagation扩展来传播当前的Bearer或Authorization Code Flow访问令牌。
由这些扩展管理的访问令牌可用作 HTTP 授权持有者令牌来访问远程服务。
另请参阅 OpenID Connect 客户端和令牌传播快速入门。
1.1. OidcClient 复制链接链接已复制到粘贴板!
添加以下依赖项:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client</artifactId>
</dependency>
quarkus-oidc-client 扩展提供 reactive io.quarkus.oidc.client.OidcClient,它可用于使用 SmallRye Mutiny Uni 和 Vert.x WebClient 获取和刷新令牌。
OidcClient 在构建时初始化,使用 IDP 令牌端点 URL,该 URL 可以自动发现或手动配置。OidcClient 使用此端点通过利用令牌授权来获取访问令牌,如 client_credentials 或 password,并使用 refresh_token 授权来刷新令牌。
1.1.1. 令牌端点配置 复制链接链接已复制到粘贴板!
默认情况下,通过将 /.well-known/openid-configuration 路径添加到配置的 quarkus.oidc-client.auth-server-url 来发现令牌端点地址。
例如,给定这个 Keycloak URL:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus
OidcClient 将发现令牌端点 URL 为 http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens。
或者,如果发现端点不可用,或者您希望在发现端点往返中保存,您可以禁用发现并配置令牌端点地址,并带有相对路径值。例如:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus quarkus.oidc-client.discovery-enabled=false # Token endpoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens quarkus.oidc-client.token-path=/protocol/openid-connect/tokens
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus
quarkus.oidc-client.discovery-enabled=false
# Token endpoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens
quarkus.oidc-client.token-path=/protocol/openid-connect/tokens
在没有发现的情况下配置令牌端点 URL 的更紧凑方法是将 quarkus.oidc-client.token-path 设置为一个绝对 URL:
quarkus.oidc-client.token-path=http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens
quarkus.oidc-client.token-path=http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens
在这种情况下,不需要设置 quarkus.oidc-client.auth-server-url 和 quarkus.oidc-client.discovery-enabled。
1.1.2. 支持的令牌授予 复制链接链接已复制到粘贴板!
主令牌授予 OidcClient 可用于获取令牌,是 client_credentials (默认) 和密码 授权。
1.1.2.1. 客户端凭证授权 复制链接链接已复制到粘贴板!
以下是如何将 OidcClient 配置为使用 client_credentials 授权:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
client_credentials 授权允许使用 quarkus.oidc-client.grant-options.client.<param-name>=<value > 为令牌请求设置额外的参数。以下是如何使用 audience 参数设置预期的令牌接收者:
1.1.2.2. 密码授权 复制链接链接已复制到粘贴板!
以下是如何将 OidcClient 配置为使用 密码 授权:
它可以通过使用 quarkus.oidc-client.grant-options.password 配置前缀来进一步自定义,类似于如何自定义客户端凭证授权。
1.1.2.3. 其他授权 复制链接链接已复制到粘贴板!
OidcClient 还可以使用授权来帮助获取令牌,该令牌需要一些无法在配置中捕获的额外输入参数。这些授权是 refresh_token (使用外部刷新令牌)、authorization_code 和两个授权来交换当前访问令牌,即 urn:ietf:params:oauth:grant-type:token-exchange 和 urn:ietf:params:oauth:grant-type:jwt-bearer。
如果您需要获取访问令牌,并将现有的刷新令牌发布到当前 Quarkus 端点,则必须使用 refresh_token 授权。此授权使用带外刷新令牌来获取新令牌集。在这种情况下,按如下所示配置 OidcClient :
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=refresh
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=refresh
然后,您可以使用 OidcClient.refreshTokens 方法提供的刷新令牌来获取访问令牌。
使用 urn:ietf:params:oauth:grant-type:token-exchange 或 urn:ietf:params:oauth:grant-type:jwt-bearer 授权,如果您要构建复杂的微服务应用,并希望避免了多个服务被传播到并使用相同的 Bearer 令牌。如需了解更多详细信息,请参阅 Quarkus REST 和 Token Propagation for RESTEasy Classic 的令牌传播。
如果出于某种原因,可能需要使用 OidcClient 支持 授权代码授权,因此您无法使用 Quarkus OIDC 扩展 来支持授权代码流。如果您实现授权代码流有很好的原因,您可以配置 OidcClient,如下所示:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=code
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=code
然后,您可以使用 OidcClient.accessTokens 方法接受额外属性映射,并传递当前 代码和 redirect_uri 参数来交换令牌的授权代码。
OidcClient 还支持 urn:openid:params:grant-type:ciba grant:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=ciba
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=ciba
然后,您可以使用 OidcClient.accessTokens 方法接受额外属性映射,并传递 auth_req_id 参数来交换令牌授权代码。
1.1.2.4. 授权范围 复制链接链接已复制到粘贴板!
您可能需要请求特定的一组范围与发布的访问令牌关联。使用专用的 quarkus.oidc-client.scopes list 属性,例如: quarkus.oidc-client.scopes=email,phone
1.1.3. 直接使用 OidcClient 复制链接链接已复制到粘贴板!
您可以直接使用 OidcClient 获取访问令牌,并将它们设置为 Bearer 方案值。
例如,假设 Quarkus 端点必须访问返回用户名的微服务。首先,创建一个 REST 客户端:
现在,使用 OidcClient 获取令牌并传播令牌:
- 1
io.quarkus.oidc.client.runtime.TokensHelper管理访问令牌获取和刷新。
1.1.4. 注入令牌 复制链接链接已复制到粘贴板!
您可以在内部 注入 使用 OidcClient 的令牌。令牌 可用于获取访问令牌并在需要时刷新它们:
1.1.5. use OidcClients 复制链接链接已复制到粘贴板!
io.quarkus.oidc.client.OidcClients 是 OidcClients 的容器 - 它包括了一个默认的 OidcClient 和 named 客户端,可以配置如下:
quarkus.oidc-client.client-enabled=false quarkus.oidc-client.jwt-secret.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.jwt-secret.client-id=quarkus-app quarkus.oidc-client.jwt-secret.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow
quarkus.oidc-client.client-enabled=false
quarkus.oidc-client.jwt-secret.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.jwt-secret.client-id=quarkus-app
quarkus.oidc-client.jwt-secret.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow
在这种情况下,默认客户端使用 client-enabled=false 属性被禁用。jwt-secret 客户端可以通过以下方式访问:
- 1
- 请参阅 Use OidcClient directly 部分中的
RestClientWithTokenHeaderParam声明。
如果您也使用 OIDC 多租户,并且每个 OIDC 租户都有自己的关联的 OidcClient,您可以使用 Vert.x RoutingContext tenant-id 属性。例如:
您还可以以编程方式创建新的 OidcClient。例如,假设您必须在启动时创建它:
现在,您可以使用此客户端,如下所示:
- 1
- 请参阅 Use OidcClient directly 部分中的
RestClientWithTokenHeaderParam声明。
1.1.6. 注入名为 OidcClient 和 token 复制链接链接已复制到粘贴板!
如果有多个配置的 OidcClient 对象,您可以通过额外的 qualifier @Named 指定 OidcClient 注入目标,而不是使用 OidcClient OidcClients :
- 1
- 请参阅 Use OidcClient directly 部分中的
RestClientWithTokenHeaderParam声明。
相同的限定符可以用来指定用于令牌注入的 Oidc :
Client
1.1.7. 在 RestClient Reactive ClientFilter 中使用 OidcClient 复制链接链接已复制到粘贴板!
添加以下 Maven 依赖:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-oidc-filter</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-oidc-filter</artifactId>
</dependency>
它还会使 io.quarkus:quarkus-oidc-client。
quarkus-rest-client-oidc-filter 扩展提供 io.quarkus.oidc.client.filter.OidcClientRequestReactiveFilter。
它的工作方式与 OidcClientRequestFilter 的效果类似(请参阅 MicroProfile RestClient 客户端过滤器 中的 OidcClient)- 它使用 OidcClient 获取访问令牌,根据需要刷新该访问令牌,并将它设置为 HTTP Authorization Bearer 方案值。区别在于,它与 Reactive RestClient 配合使用,并实施非阻塞客户端过滤器,在获取或刷新令牌时不会阻断当前的 IO 线程。
OidcClientRequestReactiveFilter 延迟初始令牌获取,直到执行前,以避免阻塞 IO 线程。
您可以使用 io.quarkus.oidc.reactive.filter.OidcClientFilter 或 注解来选择性地注册 org.eclipse.microprofile. rest.client.annotation.RegisterProviderOidcClientRequestReactiveFilter :
或者
OidcClientRequestReactiveFilter 默认使用默认的 OidcClient。可以使用 quarkus.rest-client-oidc-filter.client-name 配置属性来选择命名的 OidcClient。您还可以通过设置 @ 注释的 OidcClient Filtervalue 属性来选择 OidcClient。通过注解设置的客户端名称的优先级高于 quarkus.rest-client-oidc-filter.client-name 配置属性。例如,假设 这个 jwt-secret 名为 OIDC 客户端声明,您可以引用此客户端,如下所示:
如果您还想在每次 ProtectedResourceService#getUserName 调用时刷新令牌,则会导致 401 Unauthorized 错误,请使用 quarkus.rest-client-oidc-filter.refresh-on-unauthorized 配置属性,如下例所示:
quarkus.rest-client-oidc-filter.refresh-on-unauthorized=true
quarkus.rest-client-oidc-filter.refresh-on-unauthorized=true
另外,如果您只需要为单个端点启用此功能,请创建一个类似以下示例的自定义过滤器:
1.1.8. 在 RestClient ClientFilter 中使用 OidcClient 复制链接链接已复制到粘贴板!
添加以下 Maven 依赖:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-client-oidc-filter</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-client-oidc-filter</artifactId>
</dependency>
它还会使 io.quarkus:quarkus-oidc-client。
quarkus-resteasy-client-oidc-filter 扩展提供 io.quarkus.oidc.client.filter.OidcClientRequestFilter Jakarta REST ClientRequestFilter,它使用 OidcClient 获取访问令牌,根据需要刷新它,并将其设置为 HTTP Authorization Bearer 方案值。
默认情况下,此过滤器将获取 OidcClient,以便在初始化时获取第一对访问和刷新令牌。如果访问令牌短且刷新令牌不可用,则令牌获取应该会延迟为 quarkus.oidc-client.early-tokens-acquisition=false。
您可以使用 io.quarkus.oidc.client.filter.OidcClientFilter 或 org.eclipse.microprofile.rest.client.annotation.RegisterProvider 注解来选择性地注册 OidcClientRequestFilter :
或者
另外,如果设置了 quarkus.resteasy-client-oidc-filter.register-filter.register-filter=true 属性,OidcClientRequestFilter 可以自动注册到所有 MP Rest 或 Jakarta REST 客户端。
OidcClientRequestFilter 默认使用默认的 OidcClient。可以使用 quarkus.resteasy-client-oidc-filter.client-name 配置属性来选择命名的 OidcClient。您还可以通过设置 @ 注释的 OidcClient Filtervalue 属性来选择 OidcClient。通过注解设置的客户端名称的优先级高于 quarkus.resteasy-client-oidc-filter.client-name 配置属性。例如,假设 这个 jwt-secret 名为 OIDC 客户端声明,您可以引用此客户端,如下所示:
如果您还想在每次 ProtectedResourceService#getUserName 调用时刷新令牌,则会导致 401 Unauthorized 错误,请使用 quarkus.resteasy-client-oidc-filter.refresh-on-unauthorized 配置属性,如下例所示:
quarkus.resteasy-client-oidc-filter.refresh-on-unauthorized=true
quarkus.resteasy-client-oidc-filter.refresh-on-unauthorized=true
另外,如果您只需要为单个端点启用此功能,请创建一个类似以下示例的自定义过滤器:
1.1.9. 使用自定义 RestClient ClientFilter 复制链接链接已复制到粘贴板!
如果您愿意,您可以使用自己的自定义过滤器并注入 Tokens :
Tokens 生成者将获取并刷新令牌,自定义过滤器将决定如何使用令牌。
您还可以注入命名的 Tokens,请参阅名为 OidcClient 和 Tokens 的 Inject
1.1.10. 刷新访问令牌 复制链接链接已复制到粘贴板!
OidcClientRequestReactiveFilter、OidcClientRequestFilter 和 Tokens producers 将刷新当前的过期访问令牌(如果刷新令牌可用)。另外,quarkus.oidc-client.refresh-token-time-skew 属性可用于抢占访问令牌刷新,以避免发送可能导致 HTTP 401 错误的几乎过期的访问令牌。例如,如果此属性设置为 3S,并且访问令牌将在 3 秒内过期,则此令牌将被自动刷新。
默认情况下,OIDC 客户端会在当前请求期间刷新令牌,当它检测到令牌已过期时,如果配置了 刷新令牌时间偏移,则几乎过期。性能关键应用程序可能需要避免在传入请求期间等待可能的令牌刷新,并配置异步令牌刷新,例如:
quarkus.oidc-client.refresh-interval=1m
quarkus.oidc-client.refresh-interval=1m
- 1
- 如果当前访问令牌已过期并且必须刷新,请每分钟检查。
如果需要刷新访问令牌,但没有提供刷新令牌,则会尝试使用配置的授权(如 client_credentials )获取新令牌。
有些 OpenID Connect 供应商不会在 client_credentials 授权响应中返回刷新令牌。例如,从 Keycloak 12 开始,client_credentials 默认不会返回刷新令牌。供应商也可以限制使用刷新令牌的次数。
1.1.11. 撤销访问令牌 复制链接链接已复制到粘贴板!
如果您的 OpenId Connect 供应商(如 Keycloak)支持令牌撤销端点,则使用 OidcClient SerialrevokeAccessToken 来撤销当前的访问令牌。吊销端点 URL 与令牌请求 URI 一起发现,也可以使用 quarkus.oidc-client.revoke-path 配置。
如果将此令牌与 REST 客户端搭配使用,或者访问令牌已用于很长时间且您要刷新它,您可能希望撤销访问令牌。
这可以通过使用刷新令牌请求令牌刷新来实现。但是,如果刷新令牌不可用,您可以首先撤销它,然后请求新的访问令牌来刷新它。
1.1.12. OidcClient 身份验证 复制链接链接已复制到粘贴板!
OidcClient 必须向 OpenID Connect Provider 进行身份验证,以便 client_credentials 和其他授权请求成功。所有 OIDC 客户端身份验证 选项都被支持,例如:
client_secret_basic:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=mysecret
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=mysecret
或者
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.client-secret.value=mysecret
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.client-secret.value=mysecret
或者,使用从 CredentialsProvider 检索的 secret:
client_secret_post:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.client-secret.value=mysecret quarkus.oidc-client.credentials.client-secret.method=post
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.client-secret.value=mysecret
quarkus.oidc-client.credentials.client-secret.method=post
client_secret_jwt,签名算法是 HS256 :
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow
或者,使用从 CredentialsProvider 检索的 secret,签名算法为 HS256 :
private_key_jwt,带有内嵌在 application.properties 中的 PEM 密钥,签名算法为 RS256 :
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.jwt.key=Base64-encoded private key representation
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.key=Base64-encoded private key representation
private_key_jwt 使用 PEM 密钥文件,签名算法为 RS256 :
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.jwt.key-file=privateKey.pem
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.key-file=privateKey.pem
private_key_jwt 带有密钥存储文件,签名算法为 RS256 :
使用 client_secret_jwt 或 private_key_jwt 身份验证方法可确保没有客户端 secret overwire。
1.1.12.1. 其他 JWT 身份验证选项 复制链接链接已复制到粘贴板!
如果使用 client_secret_jwt 或 private_key_jwt 身份验证方法,则可以自定义 JWT 签名算法、密钥标识符、audience、subject 和 issuer,例如:
1.1.12.2. JWT Bearer 复制链接链接已复制到粘贴板!
RFC7523 解释了如何使用 JWT Bearer 令牌对客户端进行身份验证,请参阅 使用 JWT 进行客户端身份验证 部分。
它可以启用,如下所示:
quarkus.oidc-client.auth-server-url=${auth-server-url}
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.source=bearer
quarkus.oidc-client.auth-server-url=${auth-server-url}
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.source=bearer
接下来,JWT bearer 令牌必须作为 client_assertion 参数提供给 OIDC 客户端。
Quarkus 可以从文件系统中加载 JWT bearer 令牌。例如,在 Kubernetes 中,服务帐户令牌投射可以挂载到 /var/run/secrets/tokens 路径。然后,您需要做的都是配置 JWT bearer 令牌路径,如下所示:
quarkus.oidc-client.credentials.jwt.token-path=/var/run/secrets/tokens
quarkus.oidc-client.credentials.jwt.token-path=/var/run/secrets/tokens
- 1
- JWT bearer 令牌的路径。Quarkus 从文件系统加载新令牌,并在令牌过期时重新载入它。
其它选择是使用 OidcClient 方法获取或刷新接受额外授权参数的令牌,例如 oidcClient.getTokens (Map.of ("client_assertion", "ey…"))。
如果使用 OIDC 客户端过滤器,则必须注册将提供此断言的自定义过滤器。
以下是 Quarkus REST (以前称为 RESTEasy Reactive)自定义过滤器的示例:
以下是 RESTEasy Classic 自定义过滤器的示例:
1.1.12.3. Apple POST JWT 复制链接链接已复制到粘贴板!
Apple OpenID Connect Provider 使用 client_secret_post 方法,其中 secret 是使用 private_key_jwt 身份验证方法生成的 JWT,但使用 Apple 帐户特定的签发者和主题属性。
quarkus-oidc-client 支持非标准 client_secret_post_jwt 身份验证方法,它可以配置如下:
1.1.12.4. 双向 TLS 复制链接链接已复制到粘贴板!
有些 OpenID Connect 提供者要求客户端作为 mutual TLS (mTLS)身份验证过程的一部分进行身份验证。
quarkus-oidc-client 可以配置如下,以支持 mTLS :
1.1.13. OIDC 客户端 SPI 复制链接链接已复制到粘贴板!
当您的自定义扩展必须使用 OIDC 令牌授予的 OIDC 令牌获取 OIDC 令牌时,此扩展只能依赖于 OIDC 客户端 SPI,并根据需要让 OIDC 客户端本身获取和刷新访问令牌。
添加以下依赖项:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client-spi</artifactId>
</dependency>
下一次更新您的扩展以使用 io.quarkus.oidc.client.spi.TokenProvider CDI bean,例如:
目前,io.quarkus.oidc.client.spi.TokenProvider 仅适用于默认的 OIDC 客户端,因为自定义扩展不太可能了解多个命名 OIDC 客户端。
1.1.14. 测试 复制链接链接已复制到粘贴板!
首先,将以下依赖项添加到 test 项目中:
1.1.14.1. Wiremock 复制链接链接已复制到粘贴板!
在您的测试项目中添加以下依赖项:
- 1
- 使用正确的 Wiremock 版本。可在此找到所有可用版本。https://search.maven.org/artifact/org.wiremock/wiremock
编写基于 Wiremock 的 QuarkusTestResourceLifecycleManager,例如:
准备 REST 测试端点。您可以使用注入的 MP REST 客户端和注册的 OidcClient 过滤器测试前端端点,调用下游端点。此端点将令牌回显。例如,请参阅 主 Quarkus 存储库中的 integration-tests/oidc-client-wiremock。
设置 application.properties,例如:
最后,编写测试代码。根据上面的基于 Wiremock 的资源,第一次测试调用应返回 access_token_1 访问令牌,该令牌将在 4 秒后过期。使用 等待 静默等待大约 5 秒,现在下一个测试调用应返回 access_token_2 访问令牌,该令牌确认已过期的 access_token_1 访问令牌已被刷新。
1.1.14.2. Keycloak 复制链接链接已复制到粘贴板!
如果使用 Keycloak,您可以使用 OpenID Connect Bearer Token Integration Keycloak 部分中描述的相同方法。
1.1.15. 如何检查日志中的错误 复制链接链接已复制到粘贴板!
启用 io.quarkus.oidc.client.runtime.OidcClientImpl TRACE 级别日志记录,以查看令牌获取和刷新错误的更多详情:
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientImpl".level=TRACE quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientImpl".min-level=TRACE
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientImpl".level=TRACE
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientImpl".min-level=TRACE
启用 io.quarkus.oidc.client.runtime.OidcClientRecorder TRACE 级别日志记录,以查看 OidcClient 初始化错误的更多详情:
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".level=TRACE quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".min-level=TRACE
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".level=TRACE
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".min-level=TRACE
1.2. OIDC 请求过滤器 复制链接链接已复制到粘贴板!
您可以通过注册一个或多个 OidcRequestFilter 实现(可以更新或添加新的请求标头、自定义或分析请求正文),将 OIDC 客户端发出的 OIDC 请求过滤到 OIDC 供应商。
您可以使用单个过滤器截获请求到所有 OIDC 供应商端点,或使用 @OidcEndpoint 注解将此过滤器应用到特定的端点。例如:
OidcRequestContextProperties 可用于访问请求属性。目前,您可以使用 client_id 键访问客户端租户 id,以及一个 grant_type 键来访问 OIDC 客户端用来获取令牌的授权类型。
OidcRequestFilter 可以通过准备 io.vertx.mutiny.core.buffer.Buffer 实例并在请求上下文上设置请求正文,例如:
1.3. OIDC 响应过滤器 复制链接链接已复制到粘贴板!
您可以通过注册一个或多个 OidcResponseFilter 实现来过滤对 OIDC 客户端请求的响应,该实现可以检查响应状态、标头和正文,以记录它们或执行其他操作。
您可以有一个过滤器,截获对所有 OIDC 客户端请求的响应,或使用 @OidcEndpoint 注解将此过滤器应用到仅针对特定 OIDC 客户端请求的响应。例如:
OidcResponseFilter 可以通过准备 io.vertx.mutiny.core.buffer.Buffer 的实例来自定义响应正文,并将其设置为响应上下文的属性,例如:
1.4. Quarkus REST 的令牌传播 复制链接链接已复制到粘贴板!
quarkus-rest-client-oidc-token-propagation 扩展提供了一个 REST 客户端过滤器 io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter,它简化了身份验证信息的传播。此客户端传播当前活动请求中存在的 bearer 令牌,或作为 HTTP Authorization 标头的 Bearer 方案值从 授权代码流机制 获取的令牌。
您可以使用 io.quarkus.oidc.token.propagation.common.AccessToken 或 org.eclipse.microprofile.rest.client.annotation.RegisterProvider 注解来选择性地注册 AccessTokenRequestReactiveFilter,例如:
或者
另外,AccessTokenRequestReactiveFilter 可以支持在传播令牌前交换令牌的复杂应用程序。
如果您使用 Keycloak 或其他支持 Token Exchange 令牌授权的 OIDC 供应商,您可以配置 AccessTokenRequestReactiveFilter 来交换令牌,如下所示:
- 1
- 请注意,当 OidcClient 名称使用
io.quarkus.oidc.token.propagation.common.AccessToken"exchangeTokenClient 注解属性设置时,会忽略配置属性。exchange-token
AccessTokenRequestReactiveFilter 将使用 OidcClient 来交换当前令牌,您可以使用 quarkus.oidc-client.grant-options.exchange 设置 OpenID Connect 提供程序期望的额外交换属性。
如果您使用需要使用 JWT bearer 令牌 授权的 Azure 等提供程序来交换当前令牌,您可以配置 AccessTokenRequestReactiveFilter 来交换令牌,如下所示:
AccessTokenRequestReactiveFilter 默认使用默认的 OidcClient。可以使用 quarkus.rest-client-oidc-token-propagation.client-name 配置属性或 io.quarkus.oidc.token.propagation.common.AccessToken SerialexchangeTokenClient 注解属性来选择命名的 。
OidcClient
1.5. RESTEasy Classic 的令牌传播 复制链接链接已复制到粘贴板!
quarkus-resteasy-client-oidc-token-propagation 扩展提供了两个 Jakarta REST jakarta.ws.rs.client.ClientRequestFilter 类实现,以简化身份验证信息的传播。io.quarkus.oidc.token.propagation.AccessTokenRequestFilter 传播在当前活动请求中的 Bearer 令牌,或从 Authorization 代码流机制获取的令牌,作为 HTTP Authorization 标头的 Bearer 方案值。io.quarkus.oidc.token.propagation.JsonWebTokenRequestFilter 提供相同的功能,但也提供对 JWT 令牌的支持。
当您需要传播当前的授权代码流访问令牌时,直接令牌传播将正常工作,因为代码流访问令牌(而不是 ID 令牌)旨在为当前的 Quarkus 端点传播,以代表当前经过身份验证的用户访问远程服务。
但是,应避免直接端到端的 Bearer 令牌传播。例如,Client → Service A → Service B,其中 Service B 接收由 Client 发送到 Service A 的令牌。在这种情况下,Service B 无法区分令牌是否来自 Service A 或直接与 客户端。对于 Service B 验证令牌来自 Service A,它应该能够断言新的签发者和受众声明。
此外,复杂的应用可能需要在传播令牌之前交换或更新令牌。例如,当 Service A 访问 Service B 时,访问上下文可能会有所不同。在这种情况下,服务 A 可能会被授予一个范围范围,或者完全不同的范围来访问服务 B。
以下小节演示了 AccessTokenRequestFilter 和 JsonWebTokenRequestFilter 可以如何提供帮助。
1.5.1. RestClient AccessTokenRequestFilter 复制链接链接已复制到粘贴板!
AccessTokenRequestFilter 将所有令牌视为 Strings,因此它可以使用 JWT 和 opaque 令牌。
您可以使用 io.quarkus.oidc.token.propagation.common.AccessToken 或 org.eclipse.microprofile.rest.client.annotation.RegisterProvider 来选择性地注册 AccessTokenRequestFilter,例如:
或者
另外,如果 quarkus.resteasy-client-oidc-token-propagation.register-filter 属性设置为 true,并且 quarkus.resteasy-client-oidc-token-propagation.json-web-token 属性设置为 false,则 AccessTokenRequestFilter 属性会自动注册到所有 MP Rest 或 Jakarta REST 客户端。
1.5.1.1. 在传播前交换令牌 复制链接链接已复制到粘贴板!
如果需要在传播前交换当前的访问令牌,并使用 Keycloak 或其他支持 Token Exchange 令牌授权的 OpenID Connect 供应商,您可以配置 AccessTokenRequestFilter,如下所示:
如果您使用需要使用 JWT bearer 令牌 授权的 Azure 等提供程序来交换当前令牌,您可以配置 AccessTokenRequestFilter 以交换令牌,如下所示:
AccessTokenRequestFilter 将使用 OidcClient 来交换当前令牌,您可以使用 quarkus.oidc-client.grant-options.exchange 设置 OpenID Connect 提供程序期望的额外交换属性。
AccessTokenRequestFilter 默认使用 OidcClient。可以使用 quarkus.resteasy-client-oidc-token-propagation.client-name 配置属性来选择命名的 OidcClient。
1.5.2. RestClient JsonWebTokenRequestFilter 复制链接链接已复制到粘贴板!
如果使用 Bearer JWT 令牌,则建议使用 JsonWebTokenRequestFilter,其中这些令牌可以具有其声明,如 签发者 和 使用者 修改,以及再次修改更新的令牌(如重新签名)。它需要一个注入的 org.eclipse.microprofile.jwt.JsonWebToken,因此无法使用不透明令牌。另外,如果您的 OpenID Connect Provider 支持 Token Exchange 协议,则建议使用 AccessTokenRequestFilter,因为 JWT 和不透明 bearer 令牌都可以安全地与 AccessTokenRequestFilter 交换。
JsonWebTokenRequestFilter 可使 Service A 实施轻松更新注入的 org.eclipse.microprofile.jwt.WebToken 及新的 签发者 和 audience 声明值,并使用新的签名再次保护更新的令牌。唯一的困难是确保服务 A 具有从安全文件系统或远程安全存储(如 Vault)置备的签名密钥。
您可以使用 io.quarkus.oidc.token.propagation.JsonWebToken 或 org.eclipse.microprofile.rest.client.annotation.RegisterProvider 来选择性地注册 JsonWebTokenRequestFilter,例如:
或者
另外,如果 quarkus.resteasy-client-oidc-token-propagation.register-filter 和 quarkus.resteasy-client-oidgation.register-filter 和 quarkus.resteasy-client-oidc-token-propagation.json-web-token 属性都会自动注册 JsonWebTokenRequestFilter。
1.5.2.1. 在传播前更新令牌 复制链接链接已复制到粘贴板!
如果注入的令牌需要有其 iss (issuer)或 aud (audience)声明使用新签名再次更新和保护,则可以配置 JsonWebTokenRequestFilter,如下所示:
如前文所述,如果使用 Keycloak 或支持 Token Exchange 协议的 OpenID Connect Provider,请使用 AccessTokenRequestFilter。
1.5.3. 测试 复制链接链接已复制到粘贴板!
通常,您必须准备两个 REST 测试端点。第一个端点使用带有注册的令牌传播过滤器注入的 MP REST 客户端来调用第二个端点。
要了解它如何实现,请特别遵循 OpenID Connect 客户端和令牌传播 快速入门及其 测试 部分。
1.6. 配置参考 复制链接链接已复制到粘贴板!
1.6.1. OIDC 客户端 复制链接链接已复制到粘贴板!
build 时修复的 :在构建时修复的配置属性 - 所有其他配置属性在运行时可覆盖
| 配置属性 | Type | default |
|
在构建时修复了 如果启用了 OIDC 客户端扩展。
环境变量: | 布尔值 |
|
|
OpenID Connect (OIDC)服务器的基本 URL,例如
环境变量: | string | |
|
发现 OIDC 端点。如果没有启用,您必须单独配置 OIDC 端点 URL。
环境变量: | 布尔值 |
|
|
OIDC 动态客户端注册端点的相对路径或绝对路径或绝对路径 URL。如果
环境变量: | string | |
|
尝试初始连接到 OIDC 服务器的持续时间。例如,将持续时间设置为
环境变量: | ||
|
如果临时丢失,重试重新建立现有 OIDC 连接的次数。与
环境变量: | int |
|
|
当前 OIDC 连接请求超时的秒数。
环境变量: |
| |
|
是否应该对 worker 线程执行 DNS 查找。当您可以看到由 HTTP 请求到 OIDC 服务器阻止的 Vert.x 事件循环的日志记录警告时,请使用这个选项。
环境变量: | 布尔值 |
|
|
WebClient 使用的连接池的最大大小。
环境变量: | int | |
|
当 WebClient 获取 HTTP 302 时,自动关注重定向。当此属性只禁用到完全相同原始 URI 的单个重定向时,才允许在重定向请求期间设置了一个或多个 Cookie 时。
环境变量: | 布尔值 |
|
|
签发访问和刷新令牌的 OIDC 令牌端点;指定为相对路径或绝对 URL。如果
环境变量: | string | |
|
OIDC 令牌撤销端点的相对路径或绝对路径 URL。
环境变量: | string | |
|
应用程序的客户端 ID。每个应用都有一个客户端 ID,用于标识应用程序。如果
环境变量: | string | |
|
应用的客户端名称。它代表应用程序的人类可读描述,您可以在 OpenId Connect 供应商的仪表板中注册应用程序(客户端)时提供。例如,您可以将此属性设置为具有更详细的日志消息,记录给定客户端的活动。
环境变量: | string | |
|
唯一的 OIDC 客户端标识符。它必须在 OIDC 客户端动态创建时设置,并在所有其他情况下是可选的。
环境变量: | string | |
|
如果启用了此客户端配置。
环境变量: | 布尔值 |
|
|
访问令牌范围列表
环境变量: | 字符串列表 | |
|
访问令牌受众列表
环境变量: | 字符串列表 | |
|
刷新令牌时间偏移。如果启用了此属性,则配置的持续时间转换为秒,并在检查访问令牌是否应刷新时添加到当前时间。如果总和超过此访问令牌的过期时间,则会进行刷新。
环境变量: | ||
|
相对于当前时间的访问令牌到期期限。只有在访问令牌授权响应不包括访问令牌到期属性时,才会检查此属性。
环境变量: | ||
|
访问令牌到期时间偏移,可添加到计算的令牌到期时间。
环境变量: | ||
|
如果访问令牌 'expires_in' 属性应检查为绝对时间值,而不是相对于当前时间的持续时间。
环境变量: | 布尔值 |
|
|
授权类型
环境变量: | Client: 'client_credentials' 授权只需要 OIDC 客户端身份验证 Password: 'password' 授权需要 OIDC 客户端和用户('username' 和 'password')身份验证 Code: 'authorization_code' 授权需要 OIDC 客户端身份验证,以及至少 'code' 和 'redirect_uri' 参数,它们必须在令牌请求时传递给 OidcClient。 Exchange: 'urn:ietf:params:oauth:grant-type:token-exchange' 授权需要 OIDC 客户端身份验证,以及至少 'subject_token' 参数,该参数必须在令牌请求时传递给 OidcClient。 JWT: 'urn:ietf:params:oauth:grant-type:jwt-bearer' 授权需要 OIDC 客户端身份验证,以及至少一个 'assertion' 参数,该参数必须在令牌请求时传递给 OidcClient。
refresh: 'refresh_token' 授权需要 OIDC 客户端身份验证和刷新令牌。请注意,如果访问令牌获取响应包含刷新令牌,OidcClient 默认支持此授权。然而,在某些情况下,会提供带外的刷新令牌,例如,它可以在多个机密客户端的服务之间共享,等等。如果 'quarkus.oidc-client.grant-type' 设置为 'refresh',则 ciba: 'urn:openid:params:grant-type:ciba' 授权需要 OIDC 客户端身份验证,以及 'auth_req_id' 参数,该参数必须在令牌请求时传递给 OidcClient。 device: 'urn:ietf:params:oauth:grant-type:device_code' 授权需要 OIDC 客户端身份验证,以及 'device_code' 参数,该参数必须在令牌请求时传递给 OidcClient。 | client |
|
令牌授权响应中的访问令牌属性名称
环境变量: | string |
|
|
在令牌授权响应中刷新令牌属性名称
环境变量: | string |
|
|
令牌授权响应中的访问令牌到期属性名称
环境变量: | string |
|
|
在令牌授权响应中刷新令牌到期属性名称
环境变量: | string |
|
|
授权选项
环境变量: | Map<String,Map<String,String>> | |
|
要求所有使用"OidcClient"的过滤器在后结构初始化时获取令牌,在使用这些令牌之前可能会比较长。如果访问令牌在第一次使用并且没有刷新令牌可用之前过期,则应禁用此属性。
环境变量: | 布尔值 |
|
|
必须发送到令牌端点的自定义 HTTP 标头
环境变量: | Map<String,String> | |
|
令牌刷新间隔。默认情况下,OIDC 客户端会在当前请求期间刷新令牌,当检测到令牌已过期时,如果配置了
环境变量: | ||
| Type | default | |
|
代理的主机名或 IP 地址。
环境变量: | string | |
|
代理的端口号。默认值为
环境变量: | int |
|
|
如果代理需要身份验证,则用户名。
环境变量: | string | |
|
如果代理需要身份验证,则密码。
环境变量: | string | |
| Type | default | |
|
要使用的 TLS 配置的名称。
如果配置了名称,它会使用来自 默认 TLS 配置 不会被使用。
环境变量: | string | |
| Type | default | |
|
环境变量: | string | |
|
客户端机密值。如果设置了
环境变量: | string | |
|
CredentialsProvider bean 名称,只有在注册了多个 CredentialsProvider 时才应设置
环境变量: | string | |
|
CredentialsProvider 密钥环名称。只有在使用的 CredentialsProvider 需要密钥环名称来查找 secret 时,才需要密钥环名称,这通常是当一个 CredentialsProvider 被多个扩展共享时,才需要多个扩展从更多动态源(如 vault 实例或 secret manager)检索凭证时。
环境变量: | string | |
|
CredentialsProvider 客户端 secret 密钥
环境变量: | string | |
|
身份验证方法。如果设置了
环境变量: |
基本:
post:
post-jwt: query :客户端 ID 和 secret 作为 HTTP 查询参数提交。这个选项只支持 OIDC 扩展。 | |
|
JWT 令牌源:OIDC 供应商客户端或现有的 JWT bearer 令牌。
环境变量: |
Client: JWT 令牌由 OIDC 供应商客户端生成,以支持 bearer: JWT bearer 令牌用作客户端断言 :https://www.rfc-editor.org/rfc/rfc7523#section-2.2。 | client |
|
具有 JWT bearer 令牌(应用作客户端断言)的文件路径。此路径只能在 JWT 源(
环境变量: | path | |
|
如果提供,则表示 JWT 使用 secret 密钥进行了签名。它与键 ,
环境变量: | string | |
|
CredentialsProvider bean 名称,只有在注册了多个 CredentialsProvider 时才应设置
环境变量: | string | |
|
CredentialsProvider 密钥环名称。只有在使用的 CredentialsProvider 需要密钥环名称来查找 secret 时,才需要密钥环名称,这通常是当一个 CredentialsProvider 被多个扩展共享时,才需要多个扩展从更多动态源(如 vault 实例或 secret manager)检索凭证时。
环境变量: | string | |
|
CredentialsProvider 客户端 secret 密钥
环境变量: | string | |
|
私钥的字符串表示。如果提供,则表示 JWT 是使用 PEM 或 JWK 格式的私钥签名的。它与
环境变量: | string | |
|
如果提供,则表示 JWT 是使用 PEM 或 JWK 格式的私钥签名的。它与
环境变量: | string | |
|
如果提供,则表示 JWT 使用密钥存储中的私钥签名。它与
环境变量: | string | |
|
指定密钥存储文件的密码的参数。
环境变量: | string | |
|
私钥 ID 或别名。
环境变量: | string | |
|
私钥密码。
环境变量: | string | |
|
JWT 受众(u
环境变量: | string | |
|
添加为 JWT
环境变量: | string | |
|
添加为 JWT 的签名密钥的签发者
环境变量: | string | |
|
添加为 JWT
环境变量: | string | |
|
其他声明.
环境变量: | Map<String,String> | |
|
用于
环境变量: | string | |
|
JWT 生命周期(以秒为单位)。这个值添加到发出 JWT 的时间,以计算过期时间。
环境变量: | int |
|
|
如果为 true,则客户端身份验证令牌是 JWT bearer 授权断言。不生成 'client_assertion' 和 'client_assertion_type' 表单属性,而是只生成 'assertion'。这个选项只支持 OIDC 客户端扩展。
环境变量: | 布尔值 |
|
要写入持续时间值,请使用标准 java.time.Duration 格式。如需更多信息,请参阅 Duration#parse ()Java API 文档。
您还可以使用简化的格式,从数字开始:
- 如果值只是一个数字,它代表时间(以秒为单位)。
-
如果值为数字,后跟
ms,代表时间(毫秒)。
在其他情况下,简化的格式被转换为 java.time.Duration 格式以进行解析:
-
如果该值是一个数字,后跟
h、m或s,则前缀为PT。 -
如果值为数字,后跟
d,则会以P为前缀。
1.6.2. OIDC 令牌传播 复制链接链接已复制到粘贴板!
build 时修复的 :在构建时修复的配置属性 - 所有其他配置属性在运行时可覆盖
| 配置属性 | 类型 | default |
|
在构建时为 如果启用了 OIDC Token Reactive Propagation。
环境变量: | 布尔值 |
|
|
在构建时被修复
在
例如,您可能需要使用 请注意,这个功能依赖于重复的上下文。有关 Vert.x 重复上下文的更多信息,请参阅本指南。
环境变量: | 布尔值 |
|
|
在构建时被修复 使用 "urn:ietf:params:oauth:grant-type:token-exchange" 或 "urn:ietf:params:oauth:grant-type:jwt-bearer" 令牌授权来交换新令牌的当前令牌。
环境变量: | 布尔值 |
|
|
在构建时为
配置的 OidcClient 的名称。请注意,只有在启用
环境变量: | string |
1.7. 参考 复制链接链接已复制到粘贴板!
第 2 章 OpenID Connect 客户端和令牌传播快速入门 复制链接链接已复制到粘贴板!
了解如何使用 OpenID Connect (OIDC)和带有过滤器的 OAuth2 客户端在应用程序中获取、刷新和传播访问令牌。
有关 Quarkus 中 OIDC 客户端和 令牌传播支持的更多信息,请参阅 OpenID Connect (OIDC)和 OAuth2 客户端和过滤器参考指南。
要使用 Bearer Token Authorization 保护应用程序,请参阅 OpenID Connect (OIDC) Bearer 令牌身份验证 指南。
2.1. 先决条件 复制链接链接已复制到粘贴板!
要完成本指南,您需要:
- 大约 15 分钟
- IDE
-
正确配置了
JAVA_HOME的 JDK 17+ - Apache Maven 3.8.6 或更高版本
- 正常工作的容器运行时(Docker 或 Podman)
- 如果要使用 Quarkus CLI,可选
- 如果要构建原生可执行文件(或者使用原生容器构建,则可选的 Mandrel 或 GraalVM) https://quarkus.io/version/3.27/guides/building-native-image#configuring-graalvm
- jq 工具
2.2. 架构 复制链接链接已复制到粘贴板!
在本例中,应用程序使用两个 Jakarta REST 资源构建,即 FrontendResource 和 ProtectedResource。在这里,Frontend Resource 使用三种方法之一将访问令牌传播到 ProtectedResource :
- 它可以通过在传播令牌前使用 OIDC 客户端过滤器来获取令牌。
-
它可以使用编程创建的 OIDC 客户端来获取令牌,并通过将其作为 HTTP 授权标头值传递给 REST 客户端方法来传播令牌。
- 它可以使用 OIDC 令牌传播过滤器来传播传入的访问令牌。
FrontendResource 有八个端点:
-
/frontend/user-name-with-oidc-client-token -
/frontend/admin-name-with-oidc-client-token -
/frontend/user-name-with-oidc-client-token-header-param -
/frontend/admin-name-with-oidc-client-token-header-param -
/frontend/user-name-with-oidc-client-token-header-param-blocking -
/frontend/admin-name-with-oidc-client-token-header-param-blocking -
/frontend/user-name-with-propagated-token -
/frontend/admin-name-with-propagated-token
当 /frontend/user-name-with-oidc-client-token 或 /frontend/admin-name-with-oidc-client-token 端点被调用时,Frontend Resource 使用带有 OIDC 客户端过滤器的 REST 客户端来获取和传播访问令牌到 ProtectedResource。当 /frontend/user-name-with-oidc-client-token-header-param 或 /frontend/admin-name-with-oidc-client-token-header-param 端点被调用,Frontend Resource 使用以编程方式创建的 OIDC 客户端来获取和传播访问令牌到 ProtectedResource,方法是将其作为 HTTP Authorization 标头值传递给 REST 客户端方法。当 /frontend/user-name-with-propagated-token 或 /frontend/admin-name-with-propagated-token 端点被调用时,FrontendResource 使用带有 OIDC Token Propagation Filter 的 REST 客户端将当前的传入访问令牌传播到 ProtectedResource。
ProtectedResource 有两个端点:
-
/protected/user-name -
/protected/admin-name
两个端点返回从传入访问令牌中提取的用户名,该令牌从 FrontendResource 传播到 ProtectedResource。这些端点之间的唯一区别在于,只有在当前访问令牌具有用户角色时才允许调用 /protected/ ,只有在当前访问令牌具有 user -nameadmin 角色时才允许调用 /protected/admin-name。
2.3. 解决方案 复制链接链接已复制到粘贴板!
我们建议您按照下一小节中的说明进行操作,并逐步创建应用程序步骤。但是,您可以进入已完成的示例。
克隆 Git 存储库: git clone https://github.com/quarkusio/quarkus-quickstarts.git -b 3.27,或下载 存档。
解决方案位于 security-openid-connect-client-quickstart 目录中。
2.4. 创建 Maven 项目 复制链接链接已复制到粘贴板!
首先,您需要一个新项目。使用以下命令创建新项目:
使用 Quarkus CLI:
quarkus create app org.acme:security-openid-connect-client-quickstart \ --extension='oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest' \ --no-code cd security-openid-connect-client-quickstartquarkus create app org.acme:security-openid-connect-client-quickstart \ --extension='oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest' \ --no-code cd security-openid-connect-client-quickstartCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要创建 Gradle 项目,请添加--
gradle or--gradle-kotlin-dsl选项。有关如何安装和使用 Quarkus CLI 的更多信息,请参阅 Quarkus CLI 指南。
使用 Maven:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 要创建 Gradle 项目,请添加
-DbuildTool=gradleor-DbuildTool=gradle-kotlin-dsl选项。
对于 Windows 用户:
-
如果使用 cmd,(不要使用反向斜杠
\并将所有内容放在同一行中) -
如果使用 Powershell,则双引号中的 wrap
-D参数,如"-DprojectArtifactId=security-openid-connect-client-quickstart"
它生成 Maven 项目,导入 oidc、rest-client-oidc-filter、rest-client-oidc-token-propagation,以及 其余 扩展。
如果您已经配置了 Quarkus 项目,您可以在项目基本目录中运行以下命令来将这些扩展添加到项目中:
使用 Quarkus CLI:
quarkus extension add oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest
quarkus extension add oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,restCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 Maven:
./mvnw quarkus:add-extension -Dextensions='oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest'
./mvnw quarkus:add-extension -Dextensions='oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest'Copy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 Gradle:
./gradlew addExtension --extensions='oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest'
./gradlew addExtension --extensions='oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest'Copy to Clipboard Copied! Toggle word wrap Toggle overflow
它为构建文件添加以下扩展:
使用 Maven:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 Gradle:
implementation("io.quarkus:quarkus-oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest")implementation("io.quarkus:quarkus-oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest")Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.5. 编写应用程序 复制链接链接已复制到粘贴板!
首先实施 ProtectedResource :
ProtectedResource 返回一个来自 userName () 和 adminName () 方法的名称。名称从当前的 JsonWebToken 中提取。
接下来,添加以下 REST 客户端:
-
RestClientWithOidcClientFilter,它使用quarkus-rest-client-oidc-filter扩展提供的 OIDC 客户端过滤器来获取和传播访问令牌。 -
RestClientWithTokenHeaderParam,它接受以编程方式创建的 OidcClient 作为 HTTPAuthorization标头值而获取的令牌。 -
RestClientWithTokenPropagationFilter,它使用quarkus-rest-client-oidc-token-propagation扩展提供的 OIDC 令牌传播过滤器来获取和传播访问令牌。
添加 RestClientWithOidcClientFilter REST 客户端:
- 1
- 在 REST 客户端中注册 OIDC 客户端过滤器,以获取和传播令牌。
添加 RestClientWithTokenHeaderParam REST 客户端:
添加 RestClientWithTokenPropagationFilter REST 客户端:
- 1
- 使用 REST 客户端注册 OIDC 令牌传播过滤器,以传播传入的已存在的令牌。
不要在同一 REST 客户端中使用 RestClientWithOidcClientFilter 和 RestClientWithTokenPropagationFilter 接口,因为它们可能会发生冲突,从而导致问题。例如,OIDC 客户端过滤器可以从 OIDC 令牌传播过滤器覆盖令牌,或者传播过滤器在 none 可用时尝试传播令牌,则希望 OIDC 客户端过滤器获取新令牌。
另外,添加 OidcClientCreator 以编程方式创建 OIDC 客户端。OidcClientCreator 支持 RestClientWithTokenHeaderParam REST 客户端调用:
- 1
OidcClients可用于检索已初始化的、名为 OIDC 客户端并按需创建新的 OIDC 客户端。
现在,通过添加 FrontendResource 来完成创建应用程序:
- 1 5 6
FrontendResource使用带有 OIDC 客户端过滤器注入的RestClientWithOidcClientFilterREST 客户端来获取并传播访问令牌到ProtectedResource(当/frontend/user-name-with-oidc-client-token或/frontend/admin-name-with-oidc-client-token)。- 2 7 8
FrontendResource使用注入的RestClientWithTokenPropagationFilterREST 客户端以及 OIDC 令牌传播过滤器,以便在/frontend/user-name-with-propagated-token或/frontend/admin-name-with-propagated-token被调用时将当前的传入访问令牌传播到ProtectedResource。- 4 9 10
FrontendResource使用以编程方式创建的 OIDC 客户端来获取和传播访问令牌到ProtectedResource,方法是将其直接传递给注入的RestClientWithTokenHeaderParamREST 客户端方法作为 HTTPAuthorization标头值,即/frontend/user-name-with-oidc-client-token-header-param或/frontend/admin-name-with-oidc-client-token-header-param。- 11 12
- 有时,在向令牌传播令牌之前,可能需要以阻止方式获取令牌。本例演示了如何在这样的情形中获取令牌。
- 3
- 当 OIDC 客户端直接使用时,
io.quarkus.oidc.client.runtime.TokensHelper是一个非常有用的工具,没有 OIDC 客户端过滤器。要使用TokensHelper,请将 OIDC Client 传递给它以获取令牌,并且TokensHelper获取令牌,并在需要时以线程安全的方式刷新它们。
最后,添加 Jakarta REST ExceptionMapper :
此例外映射程序仅用于在测试期间验证 ProtectedResource 是否在令牌没有预期的角色时返回 403。如果没有此映射程序,Quarkus REST (以前称为 RESTEasy Reactive)可以正确地将来自 REST 客户端调用的异常转换为 500,以避免泄漏下游资源(如 ProtectedResource )的信息。但是,在测试中,无法断言 500 是由授权异常导致的,而不是一些内部错误。
2.6. 配置应用程序 复制链接链接已复制到粘贴板!
准备代码,您可以配置应用程序:
上述配置引用 Keycloak,由 ProtectedResource 用于验证传入的访问令牌,并且 OidcClient 使用 OidcClient 使用密码 授权来获取用户 alice 的令牌。两个 REST 客户端都指向 ProtectedResource 的 HTTP 地址。
将 %prod. 配置集前缀添加到 quarkus.oidc.auth-server-url 可确保当应用程序以 dev 或 test 模式运行时,为您启动 Dev Services。有关更多信息,请参阅 以 dev 模式运行应用。
2.7. 启动并配置 Keycloak 服务器 复制链接链接已复制到粘贴板!
当您以 dev 或 test 模式运行应用程序时,不要启动 Keycloak 服务器 ; Keycloak 的 Dev Services 启动容器。有关更多信息,请参阅 以 dev 模式运行应用。确保将 realm 配置文件放在 classpath 上,它位于 target/classes 目录中。此放置可确保文件在 dev 模式中自动导入。但是,如果您已构建了一个 完整的解决方案,则不需要将 realm 文件添加到类路径,因为构建过程已完成。
要启动 Keycloak 服务器,您可以使用 Docker 并运行以下命令:
docker run --name keycloak -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin -p 8180:8080 quay.io/keycloak/keycloak:{keycloak.version} start-dev
docker run --name keycloak -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin -p 8180:8080 quay.io/keycloak/keycloak:{keycloak.version} start-dev
将 {keycloak.version} 设置为 26.3.0 或更高版本。
您可以在 localhost:8180 访问您的 Keycloak 服务器。
以 admin 用户身份登录,以访问 Keycloak 管理控制台。密码是 admin。
导入 realm 配置文件以创建 新域。如需了解更多详细信息,请参阅关于如何创建新域的 Keycloak 文档。https://www.keycloak.org/docs/latest/server_admin/index.html#_create-realm
此 quarkus 域文件添加 frontend 客户端,以及 alice 和 admin 用户。alice 具有 用户角色。admin 同时具有 user 和 admin 角色。
2.8. 以 dev 模式运行应用程序 复制链接链接已复制到粘贴板!
要在 dev 模式下运行应用程序,请使用:
使用 Quarkus CLI:
quarkus dev
quarkus devCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 Maven:
./mvnw quarkus:dev
./mvnw quarkus:devCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 Gradle:
./gradlew --console=plain quarkusDev
./gradlew --console=plain quarkusDevCopy to Clipboard Copied! Toggle word wrap Toggle overflow
Keycloak 的 dev Services 启动 Keycloak 容器,并导入 quarkus-realm.json。
打开位于 /q/dev-ui 的 Dev UI,然后点击 OpenID Connect Dev UI 卡中的 Keycloak 供应商 链接。
提示时,登录到 OpenID Connect Dev UI 提供的 单个页面应用程序 :
以
admin用户身份使用密码admin登录。此用户同时具有admin和 用户角色。-
访问
/frontend/user-name-with-propagated-token,它返回200。 -
访问
/frontend/admin-name-with-propagated-token,它返回200。
-
访问
注销,然后以
alice用户身份使用密码alice重新登录。此用户具有用户角色。-
访问
/frontend/user-name-with-propagated-token,它返回200。 -
访问
/frontend/admin-name-with-propagated-token,它返回403。
-
访问
您已测试了 FrontendResource 可以从 OpenID Connect Dev UI 传播访问令牌。
2.9. 在 JVM 模式下运行应用程序 复制链接链接已复制到粘贴板!
在以 dev 模式探索应用后,您可以将其作为标准 Java 应用运行。
首先,编译它:
使用 Quarkus CLI:
quarkus build
quarkus buildCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 Maven:
./mvnw install
./mvnw installCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 Gradle:
./gradlew build
./gradlew buildCopy to Clipboard Copied! Toggle word wrap Toggle overflow
然后运行它:
java -jar target/quarkus-app/quarkus-run.jar
java -jar target/quarkus-app/quarkus-run.jar
2.10. 以原生模式运行应用程序 复制链接链接已复制到粘贴板!
您可以将此演示编译为原生代码,不需要修改。
这意味着,您不再需要在生产环境中安装 JVM,因为运行时技术包含在生成的二进制中,并优化以最小资源运行。
编译需要更长的时间,因此默认关闭此步骤。要再次构建,请启用 原生 配置集:
使用 Quarkus CLI:
quarkus build --native
quarkus build --nativeCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 Maven:
./mvnw install -Dnative
./mvnw install -DnativeCopy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 Gradle:
./gradlew build -Dquarkus.native.enabled=true
./gradlew build -Dquarkus.native.enabled=trueCopy to Clipboard Copied! Toggle word wrap Toggle overflow
在稍等片刻后,构建完成后,您可以直接运行原生二进制文件:
./target/security-openid-connect-quickstart-1.0.0-SNAPSHOT-runner
./target/security-openid-connect-quickstart-1.0.0-SNAPSHOT-runner
2.11. 测试应用程序 复制链接链接已复制到粘贴板!
有关以 dev 模式测试应用程序的更多信息,请参阅前面 在 dev mode 中运行该应用。
您可以使用 curl 来测试以 JVM 或原生模式启动的应用程序。
获取 alice 的访问令牌:
使用此令牌调用 /frontend/user-name-with-propagated-token。此命令返回 200 状态代码和名称 alice :
curl -i -X GET \ http://localhost:8080/frontend/user-name-with-propagated-token \ -H "Authorization: Bearer "$access_token
curl -i -X GET \
http://localhost:8080/frontend/user-name-with-propagated-token \
-H "Authorization: Bearer "$access_token
使用相同的令牌调用 /frontend/admin-name-with-propagated-token。与前面的命令不同,这个命令会返回 403,因为 alice 只有一个 用户角色 :
curl -i -X GET \ http://localhost:8080/frontend/admin-name-with-propagated-token \ -H "Authorization: Bearer "$access_token
curl -i -X GET \
http://localhost:8080/frontend/admin-name-with-propagated-token \
-H "Authorization: Bearer "$access_token
接下来,获取 admin 的访问令牌:
使用此令牌调用 /frontend/user-name-with-propagated-token。此命令返回 200 状态代码和名称 admin :
curl -i -X GET \ http://localhost:8080/frontend/user-name-with-propagated-token \ -H "Authorization: Bearer "$access_token
curl -i -X GET \
http://localhost:8080/frontend/user-name-with-propagated-token \
-H "Authorization: Bearer "$access_token
使用相同的令牌调用 /frontend/admin-name-with-propagated-token。此命令还会返回 200 状态代码和名称 admin,因为 具有 admin user 和 admin 角色:
curl -i -X GET \ http://localhost:8080/frontend/admin-name-with-propagated-token \ -H "Authorization: Bearer "$access_token
curl -i -X GET \
http://localhost:8080/frontend/admin-name-with-propagated-token \
-H "Authorization: Bearer "$access_token
接下来,检查 FrontendResource 方法,它不会传播现有的令牌,而是使用 OidcClient 获取和传播令牌。如上所示,OidcClient 配置为获取 alice 用户的令牌。
curl -i -X GET \ http://localhost:8080/frontend/user-name-with-oidc-client-token
curl -i -X GET \
http://localhost:8080/frontend/user-name-with-oidc-client-token
此命令返回 200 状态代码和名称 alice。
curl -i -X GET \ http://localhost:8080/frontend/admin-name-with-oidc-client-token
curl -i -X GET \
http://localhost:8080/frontend/admin-name-with-oidc-client-token
与前面的命令不同,此命令会返回 403 状态代码。
接下来,测试以编程方式创建的 OIDC 客户端是否在 reactive 和 imperative (阻塞)模式中同时使用 RestClientWithTokenHeaderParam 正确获取并传播令牌。
调用 /user-name-with-oidc-client-token-header-param。此命令返回 200 状态代码和名称 alice :
curl -i -X GET \ http://localhost:8080/frontend/user-name-with-oidc-client-token-header-param
curl -i -X GET \
http://localhost:8080/frontend/user-name-with-oidc-client-token-header-param
调用 /admin-name-with-oidc-client-token-header-param。与前面的命令不同,这个命令会返回一个 403 状态代码:
curl -i -X GET \ http://localhost:8080/frontend/admin-name-with-oidc-client-token-header-param
curl -i -X GET \
http://localhost:8080/frontend/admin-name-with-oidc-client-token-header-param
接下来,在阻塞模式下测试使用 OIDC 客户端的端点。
调用 /user-name-with-oidc-client-token-header-param-blocking。此命令返回 200 状态代码和名称 alice :
curl -i -X GET \ http://localhost:8080/frontend/user-name-with-oidc-client-token-header-param-blocking
curl -i -X GET \
http://localhost:8080/frontend/user-name-with-oidc-client-token-header-param-blocking
调用 /admin-name-with-oidc-client-token-header-param-blocking。与前面的命令不同,这个命令会返回一个 403 状态代码:
curl -i -X GET \ http://localhost:8080/frontend/admin-name-with-oidc-client-token-header-param-blocking
curl -i -X GET \
http://localhost:8080/frontend/admin-name-with-oidc-client-token-header-param-blocking