OpenID Connect (OIDC) and OAuth2 client and filters
您可以使用 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 Authorization Bearer 令牌来访问远程服务。 另请参见 OpenID Connect client and token propagation quickstart。
- OidcClient
- Token endpoint configuration
- Supported token grants
- Use OidcClient directly
- Inject tokens
- Use OidcClients
- Inject named OidcClient and tokens
- Use OidcClient in RestClient Reactive ClientFilter
- Use OidcClient in RestClient ClientFilter
- Use a custom RestClient ClientFilter
- Refreshing access tokens
- Revoking access tokens
- OidcClient authentication
- Testing
- How to check the errors in the logs
- OIDC request filters
- Token Propagation Reactive
- Token Propagation
- Token Propagation Reactive
- GraphQL client integration
- Configuration reference
- References
OidcClient
添加以下依赖项:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client</artifactId>
</dependency>
quarkus-oidc-client
扩展提供了一个反应式 io.quarkus.oidc.client.OidcClient
,该反应式可用于通过 SmallRye Mutiny Uni
和 Vert.x WebClient
来获取和刷新令牌。
OidcClient
在构建时通过 IDP 令牌端点 URL 初始化,该 URL 可以自动发现或手动配置。OidcClient
使用此端点通过令牌授权(如 client_credentials
或 password
)获取访问令牌,并通过 refresh_token
授权刷新令牌。
Token endpoint configuration
默认情况下,令牌端点地址通过向已配置的 quarkus.oidc-client.auth-server-url
中添加 /.well-known/openid-configuration
路径来发现。
例如,给定此 Keycloak URL:
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
无需发现就配置令牌端点 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.auth-server-url
和 quarkus.oidc-client.discovery-enabled
。
Supported token grants
OidcClient
用于获取令牌的主要令牌授权是 client_credentials
(默认)和 password
授权。
Client credentials grant
下面是如何配置 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
client_credentials
授权允许通过使用 quarkus.oidc-client.grant-options.client.<param-name>=<value>
来设置令牌请求的额外参数。以下是使用 audience
参数设置预期令牌接收者的方法:
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' is a shortcut for `client_credentials`
quarkus.oidc-client.grant.type=client
quarkus.oidc-client.grant-options.client.audience=https://example.com/api
Password grant
以下是如何配置 OidcClient
以使用 password
授权:
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=password
quarkus.oidc-client.grant-options.password.username=alice
quarkus.oidc-client.grant-options.password.password=alice
它可以通过使用 quarkus.oidc-client.grant-options.password
配置前缀进行进一步自定义,类似于可以自定义客户端凭证授权的方式。
Other grants
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
然后,你可以将 OidcClient.refreshTokens
方法与提供的刷新令牌一起使用以获取访问令牌。
如果你正在构建一个复杂的微服务应用程序并在多个服务中使用同一个 Bearer`令牌,则可能需要使用 `urn:ietf:params:oauth:grant-type:token-exchange
或 urn:ietf:params:oauth:grant-type:jwt-bearer
授权。有关更多详细信息,请参阅 Token Propagation in MicroProfile RestClient Reactive filter 和 Token Propagation in MicroProfile RestClient filter。
如果你由于某种原因无法使用 Quarkus OIDC extension 来支持授权代码流程,则可能需要使用 OidcClient
来支持 authorization code
授权。如果你有非常充分的理由来实现授权代码流程,则可以按如下方式配置 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
然后,可以使用 OidcClient.accessTokens
方法来接受额外属性的 Map,并传递当前 code
和 redirect_uri
参数以交换授权代码以获取令牌。
OidcClient
也支持 urn:openid:params: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
方法来接受 Map 的额外属性,并传递 auth_req_id
参数以交换令牌授权代码。
Use OidcClient directly
你可以直接使用 OidcClient
来获取访问令牌,并将它们设置为 HTTP Authorization
头作为 Bearer
方案值。
例如,让我们假设 Quarkus 端点必须访问返回用户名微服务。首先,创建一个 REST 客户端:
package org.acme.security.openid.connect.client;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.smallrye.mutiny.Uni;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@RegisterRestClient
@Path("/")
public interface RestClientWithTokenHeaderParam {
@GET
@Produces("text/plain")
@Path("userName")
Uni<String> getUserName(@HeaderParam("Authorization") String authorization);
}
现在,使用 OidcClient
来获取令牌并传播它们:
package org.acme.security.openid.connect.client;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import io.quarkus.oidc.client.runtime.TokensHelper;
import io.quarkus.oidc.client.OidcClient;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@Path("/service")
public class OidcClientResource {
@Inject
OidcClient client;
TokensHelper tokenHelper = new TokensHelper(); 1
@Inject
@RestClient
RestClientWithTokenHeaderParam restClient;
@GET
@Path("user-name")
@Produces("text/plain")
public Uni<String> getUserName() {
return tokenHelper.getTokens(client).onItem()
.transformToUni(tokens -> restClient.getUserName("Bearer " + tokens.getAccessToken()));
}
}
1 | io.quarkus.oidc.client.runtime.TokensHelper 管理访问令牌的获取和刷新。 |
Inject tokens
你可以注入 Tokens
,其在内部使用 OidcClient
。 Tokens
可用于获取访问令牌并在必要时刷新它们:
import jakarta.inject.PostConstruct;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import io.quarkus.oidc.client.Tokens;
@Path("/service")
public class OidcClientResource {
@Inject Tokens tokens;
@GET
public String getResponse() {
// Get the access token, which might have been refreshed.
String accessToken = tokens.getAccessToken();
// Use the access token to configure MP RestClient Authorization header/etc
}
}
Use OidcClients
io.quarkus.oidc.client.OidcClients
是 OidcClient`s - it includes a default `OidcClient
的容器,并且可以这样配置具有名称的客户端:
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
客户端可以像这样进行访问:
import org.eclipse.microprofile.rest.client.inject.RestClient;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import io.smallrye.mutiny.Uni;
import io.quarkus.oidc.client.OidcClient;
import io.quarkus.oidc.client.OidcClients;
import io.quarkus.oidc.client.runtime.TokensHelper;
@Path("/clients")
public class OidcClientResource {
@Inject
OidcClients clients;
TokensHelper tokenHelper = new TokensHelper();
@Inject
@RestClient
RestClientWithTokenHeaderParam restClient; 1
@GET
@Path("user-name")
@Produces("text/plain")
public Uni<String> getUserName() {
OidcClient client = clients.getClient("jwt-secret");
return tokenHelper.getTokens(client).onItem()
.transformToUni(tokens -> restClient.getUserName("Bearer " + tokens.getAccessToken()));
}
}
1 | 请参阅 Use OidcClient directly 部分中的 RestClientWithTokenHeaderParam 声明。 |
如果同时使用 OIDC multitenancy,每个 OIDC 租户又都有各自的关联
|
还可以编程地创建新的 OidcClient
。例如,假设必须在启动时创建它:
package org.acme.security.openid.connect.client;
import java.util.Map;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import io.quarkus.oidc.client.OidcClient;
import io.quarkus.oidc.client.OidcClientConfig;
import io.quarkus.oidc.client.OidcClientConfig.Grant.Type;
import io.quarkus.oidc.client.OidcClients;
import io.quarkus.runtime.StartupEvent;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
@ApplicationScoped
public class OidcClientCreator {
@Inject
OidcClients oidcClients;
@ConfigProperty(name = "quarkus.oidc.auth-server-url")
String oidcProviderAddress;
private volatile OidcClient oidcClient;
public void startup(@Observes StartupEvent event) {
createOidcClient().subscribe().with(client -> {oidcClient = client;});
}
public OidcClient getOidcClient() {
return oidcClient;
}
private Uni<OidcClient> createOidcClient() {
OidcClientConfig cfg = new OidcClientConfig();
cfg.setId("myclient");
cfg.setAuthServerUrl(oidcProviderAddress);
cfg.setClientId("backend-service");
cfg.getCredentials().setSecret("secret");
cfg.getGrant().setType(Type.PASSWORD);
cfg.setGrantOptions(Map.of("password",
Map.of("username", "alice", "password", "alice")));
return oidcClients.newClient(cfg);
}
}
现在,可以按如下方式使用此客户端:
import org.eclipse.microprofile.rest.client.inject.RestClient;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import io.smallrye.mutiny.Uni;
import io.quarkus.oidc.client.runtime.TokensHelper;
@Path("/clients")
public class OidcClientResource {
@Inject
OidcClientCreator oidcClientCreator;
TokensHelper tokenHelper = new TokensHelper();
@Inject
@RestClient
RestClientWithTokenHeaderParam restClient; 1
@GET
@Path("user-name")
@Produces("text/plain")
public Uni<String> getUserName() {
return tokenHelper.getTokens(oidcClientCreator.getOidcClient()).onItem()
.transformToUni(tokens -> restClient.getUserName("Bearer " + tokens.getAccessToken()));
}
}
1 | 请参阅 Use OidcClient directly 部分中的 RestClientWithTokenHeaderParam 声明。 |
Inject named OidcClient and tokens
在配置了多个 OidcClient
对象的情况下,可以使用额外的限定符 @NamedOidcClient`而不是 `OidcClients`来指定 `OidcClient
注入目标:
package org.acme.security.openid.connect.client;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import io.smallrye.mutiny.Uni;
import io.quarkus.oidc.client.runtime.TokensHelper;
@Path("/clients")
public class OidcClientResource {
@Inject
@NamedOidcClient("jwt-secret")
OidcClient client;
TokensHelper tokenHelper = new TokensHelper();
@Inject
@RestClient
RestClientWithTokenHeaderParam restClient; 1
@GET
@Path("user-name")
@Produces("text/plain")
public Uni<String> getUserName() {
return tokenHelper.getTokens(client).onItem()
.transformToUni(tokens -> restClient.getUserName("Bearer " + tokens.getAccessToken()));
}
}
1 | 请参阅 Use OidcClient directly 部分中的 RestClientWithTokenHeaderParam 声明。 |
可以使用相同的限定符来指定用于 Tokens
注入的 OidcClient
:
@Provider
@Priority(Priorities.AUTHENTICATION)
@RequestScoped
public class OidcClientRequestCustomFilter implements ClientRequestFilter {
@Inject
@NamedOidcClient("jwt-secret")
Tokens tokens;
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + tokens.getAccessToken());
}
}
Use OidcClient in RestClient Reactive ClientFilter
添加以下 Maven 依赖项:
<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
(请参阅 Use OidcClient in MicroProfile RestClient client filter)- 它使用 OidcClient
获取访问令牌,根据需要刷新令牌并将其设置为 HTTP Authorization
Bearer
架构值。差异在于它使用 Reactive RestClient 并实现了一个非阻塞客户端过滤器,在获取或刷新令牌时不会阻塞当前 IO 线程。
OidcClientRequestReactiveFilter
会延迟初始令牌获取,直到执行它来避免阻塞 IO 线程。
可以使用 io.quarkus.oidc.client.reactive.filter.OidcClientFilter
或 org.eclipse.microprofile.rest.client.annotation.RegisterProvider
注释有选择地注册 OidcClientRequestReactiveFilter
:
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;
import io.smallrye.mutiny.Uni;
@RegisterRestClient
@OidcClientFilter
@Path("/")
public interface ProtectedResourceService {
@GET
Uni<String> getUserName();
}
或
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.reactive.filter.OidcClientRequestReactiveFilter;
import io.smallrye.mutiny.Uni;
@RegisterRestClient
@RegisterProvider(OidcClientRequestReactiveFilter.class)
@Path("/")
public interface ProtectedResourceService {
@GET
Uni<String> getUserName();
}
OidcClientRequestReactiveFilter
默认使用默认 OidcClient
。可以用 quarkus.rest-client-oidc-filter.client-name
配置属性选择已命名的 OidcClient
。还可以通过设置 @OidcClientFilter
注释的 value
属性来选择 OidcClient
。通过注释设置的客户端名称的优先级高于 quarkus.rest-client-oidc-filter.client-name
配置属性。例如,给定 this jwt-secret
命名的 OIDC 客户端声明,可以按如下方式引用此客户端:
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;
import io.smallrye.mutiny.Uni;
@RegisterRestClient
@OidcClientFilter("jwt-secret")
@Path("/")
public interface ProtectedResourceService {
@GET
Uni<String> getUserName();
}
Use OidcClient in RestClient ClientFilter
添加以下 Maven 依赖项:
<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
:
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;
@RegisterRestClient
@OidcClientFilter
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
或
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientRequestFilter;
@RegisterRestClient
@RegisterProvider(OidcClientRequestFilter.class)
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
或者,如果设置了 quarkus.resteasy-client-oidc-filter.register-filter=true
属性,OidcClientRequestFilter
可以在所有 MP Rest 或 Jakarta REST 客户端中自动注册。
OidcClientRequestFilter
默认使用默认 OidcClient
。可以用 quarkus.resteasy-client-oidc-filter.client-name
配置属性选择已命名的 OidcClient
。还可以通过设置 @OidcClientFilter
注释的 value
属性来选择 OidcClient
。通过注释设置的客户端名称的优先级高于 quarkus.resteasy-client-oidc-filter.client-name
配置属性。例如,给定 this jwt-secret
命名的 OIDC 客户端声明,可以按如下方式引用此客户端:
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;
@RegisterRestClient
@OidcClientFilter("jwt-secret")
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
Use a custom RestClient ClientFilter
如果愿意,可以按如下方式使用自己的自定义过滤器和注入 Tokens
:
import io.quarkus.oidc.client.Tokens;
@Provider
@Priority(Priorities.AUTHENTICATION)
public class OidcClientRequestCustomFilter implements ClientRequestFilter {
@Inject
Tokens tokens;
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + tokens.getAccessToken());
}
}
`Tokens`制作者将获取并刷新令牌,而自定义过滤器将决定如何以及何时使用令牌。
您还可以注入已命名的 Tokens
,请参阅 Inject named OidcClient and Tokens
Refreshing access tokens
OidcClientRequestReactiveFilter
、`OidcClientRequestFilter`和 `Tokens`制作者将刷新当前过期的访问令牌(如果存在刷新令牌)。此外,`quarkus.oidc-client.refresh-token-time-skew`属性可用于先发性访问令牌刷新,以避免发送可能导致 HTTP 401 错误的临近过期的访问令牌。例如,如果此属性设置为 `3S`并且访问令牌将在不到 3 秒内过期,那么此令牌将被自动刷新。
如果需要刷新访问令牌,但没有可用的刷新令牌,则会尝试使用已配置的授权(例如 client_credentials
)来获取新令牌。
某些 OpenID Connect 提供程序不会在 `client_credentials`授权响应中返回刷新令牌。例如,从 Keycloak 12 开始,默认情况下不会为 `client_credentials`返回刷新令牌。这些提供程序还可能限制使用刷新令牌的次数。
Revoking access tokens
如果您的 OpenId Connect 提供程序(例如 Keycloak)支持令牌撤销端点,则可使用 `OidcClient#revokeAccessToken`撤销当前访问令牌。撤销端点 URL 将与令牌请求 URI 一起发现,或可使用 `quarkus.oidc-client.revoke-path`进行配置。
如果您将访问令牌与 REST 客户端一起使用时失败,并显示 HTTP `401`状态代码或者访问令牌已长时间使用且您希望刷新该令牌,则可能需要撤销该访问令牌。
可以通过使用刷新令牌请求令牌刷新来实现这一点。但是,如果刷新令牌不可用,您可以先撤销它然后再请求新访问令牌来刷新它。
OidcClient authentication
`OidcClient`必须向 OpenID Connect Provider 进行身份验证,以便 `client_credentials`和其他授权请求成功。所有 OIDC Client Authentication选项均受支持,例如:
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.client-secret.value=mysecret
或使用从 CredentialsProvider中检索的密钥:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
# This key is used to retrieve a secret from the map of credentials returned from CredentialsProvider
quarkus.oidc-client.credentials.client-secret.provider.key=mysecret-key
# This is the keyring provided to the CredentialsProvider when looking up the secret, set only if required by the CredentialsProvider implementation
quarkus.oidc.credentials.client-secret.provider.keyring-name=oidc
# Set it only if more than one CredentialsProvider can be registered
quarkus.oidc-client.credentials.client-secret.provider.name=oidc-credentials-provider
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
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
或使用从 CredentialsProvider中检索的密钥,签名算法是 HS256
:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
# This is a key that will be used to retrieve a secret from the map of credentials returned from CredentialsProvider
quarkus.oidc-client.credentials.jwt.secret-provider.key=mysecret-key
# This is the keyring provided to the CredentialsProvider when looking up the secret, set only if required by the CredentialsProvider implementation
quarkus.oidc.credentials.client-secret.provider.keyring-name=oidc
# Set it only if more than one CredentialsProvider can be registered
quarkus.oidc-client.credentials.jwt.secret-provider.name=oidc-credentials-provider
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
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
private_key_jwt`使用 keystore 文件,签名算法是 `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-store-file=keystore.jks
quarkus.oidc-client.credentials.jwt.key-store-password=mypassword
quarkus.oidc-client.credentials.jwt.key-password=mykeypassword
# Private key alias inside the keystore
quarkus.oidc-client.credentials.jwt.key-id=mykeyAlias
使用 `client_secret_jwt`或 `private_key_jwt`认证方法可确保没有客户端密钥通过网络传输。
Additional JWT authentication options
如果`client_secret_jwt`或`private_key_jwt`身份验证方法中任一方法被使用,则 JWT 签名算法、密钥标识符、目标受众、主体和颁发者可以自定义,例如:
# private_key_jwt client authentication
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
# This is a token key identifier 'kid' header - set it if your OpenID Connect provider requires it.
# Note that if the key is represented in a JSON Web Key (JWK) format with a `kid` property, then
# using 'quarkus.oidc-client.credentials.jwt.token-key-id' is unnecessary.
quarkus.oidc-client.credentials.jwt.token-key-id=mykey
# Use the RS512 signature algorithm instead of the default RS256
quarkus.oidc-client.credentials.jwt.signature-algorithm=RS512
# The token endpoint URL is the default audience value; use the base address URL instead:
quarkus.oidc-client.credentials.jwt.audience=${quarkus.oidc-client.auth-server-url}
# custom subject instead of the client ID:
quarkus.oidc-client.credentials.jwt.subject=custom-subject
# custom issuer instead of the client ID:
quarkus.oidc-client.credentials.jwt.issuer=custom-issuer
JWT Bearer
RFC7523解释了如何使用 JWT Bearer 令牌对客户端进行身份验证,有关详细信息,请参见 Using JWTs for Client Authentication部分。
可以通过以下方式启用它:
quarkus.oidc-client.auth-server-url=${auth-server-url}
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.source=bearer
接下来,必须将 JWT 承载令牌作为 `client_assertion`参数提供给 OIDC 客户端。
你可以使用 OidcClient`方法来获取或刷新接受附加授予参数的令牌,例如 `oidcClient.getTokens(Map.of("client_assertion", "ey…"))
。
如果你要使用 OIDC 客户端过滤器,则必须注册一个自定义过滤器,该过滤器将提供此断言。
以下是一个 Quarkus REST(以前称为 RESTEasy Reactive)自定义过滤器的示例:
package io.quarkus.it.keycloak;
import java.util.Map;
import io.quarkus.oidc.client.reactive.filter.runtime.AbstractOidcClientRequestReactiveFilter;
import io.quarkus.oidc.common.runtime.OidcConstants;
import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
@Priority(Priorities.AUTHENTICATION)
public class OidcClientRequestCustomFilter extends AbstractOidcClientRequestReactiveFilter {
@Override
protected Map<String, String> additionalParameters() {
return Map.of(OidcConstants.CLIENT_ASSERTION, "ey...");
}
}
以下是一个 RestEasy Classic 自定义过滤器的示例:
package io.quarkus.it.keycloak;
import java.util.Map;
import io.quarkus.oidc.client.filter.runtime.AbstractOidcClientRequestFilter;
import io.quarkus.oidc.common.runtime.OidcConstants;
import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
@Priority(Priorities.AUTHENTICATION)
public class OidcClientRequestCustomFilter extends AbstractOidcClientRequestFilter {
@Override
protected Map<String, String> additionalParameters() {
return Map.of(OidcConstants.CLIENT_ASSERTION, "ey...");
}
}
Apple POST JWT
Apple OpenID Connect Provider 使用 `client_secret_post`方法,其中密钥是使用 `private_key_jwt`身份验证方法生成的 JWT,但具有 Apple 帐户特定的颁发者和主题属性。
`quarkus-oidc-client`支持非标准 `client_secret_post_jwt`身份验证方法,可以按以下方式进行配置:
quarkus.oidc-client.auth-server-url=${apple.url}
quarkus.oidc-client.client-id=${apple.client-id}
quarkus.oidc-client.credentials.client-secret.method=post-jwt
quarkus.oidc-client.credentials.jwt.key-file=ecPrivateKey.pem
quarkus.oidc-client.credentials.jwt.signature-algorithm=ES256
quarkus.oidc-client.credentials.jwt.subject=${apple.subject}
quarkus.oidc-client.credentials.jwt.issuer=${apple.issuer}
Mutual TLS
某些 OpenID Connect Providers 要求作为双向 TLS (mTLS
) 身份验证过程的一部分对客户端进行身份验证。
可以按以下方式配置 quarkus-oidc-client`来支持 `mTLS
:
quarkus.oidc-client.tls.verification=certificate-validation
# Keystore configuration
quarkus.oidc-client.tls.key-store-file=client-keystore.jks
quarkus.oidc-client.tls.key-store-password=${key-store-password}
# Add more keystore properties if needed:
#quarkus.oidc-client.tls.key-store-alias=keyAlias
#quarkus.oidc-client.tls.key-store-alias-password=keyAliasPassword
# Truststore configuration
quarkus.oidc-client.tls.trust-store-file=client-truststore.jks
quarkus.oidc-client.tls.trust-store-password=${trust-store-password}
# Add more truststore properties if needed:
#quarkus.oidc-client.tls.trust-store-alias=certAlias
Testing
首先将以下依赖项添加到测试项目:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
Wiremock
向测试项目中添加以下依赖项:
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<scope>test</scope>
</dependency>
编写一个基于 Wiremock 的 QuarkusTestResourceLifecycleManager
,例如:
package io.quarkus.it.keycloak;
import static com.github.tomakehurst.wiremock.client.WireMock.matching;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import java.util.HashMap;
import java.util.Map;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.core.Options.ChunkedEncodingPolicy;
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
public class KeycloakRealmResourceManager implements QuarkusTestResourceLifecycleManager {
private WireMockServer server;
@Override
public Map<String, String> start() {
server = new WireMockServer(wireMockConfig().dynamicPort().useChunkedTransferEncoding(ChunkedEncodingPolicy.NEVER));
server.start();
server.stubFor(WireMock.post("/tokens")
.withRequestBody(matching("grant_type=password&username=alice&password=alice"))
.willReturn(WireMock
.aResponse()
.withHeader("Content-Type", "application/json")
.withBody(
"{\"access_token\":\"access_token_1\", \"expires_in\":4, \"refresh_token\":\"refresh_token_1\"}")));
server.stubFor(WireMock.post("/tokens")
.withRequestBody(matching("grant_type=refresh_token&refresh_token=refresh_token_1"))
.willReturn(WireMock
.aResponse()
.withHeader("Content-Type", "application/json")
.withBody(
"{\"access_token\":\"access_token_2\", \"expires_in\":4, \"refresh_token\":\"refresh_token_1\"}")));
Map<String, String> conf = new HashMap<>();
conf.put("keycloak.url", server.baseUrl());
return conf;
}
@Override
public synchronized void stop() {
if (server != null) {
server.stop();
server = null;
}
}
}
准备 REST 测试端点。你可以拥有测试前端端点,它使用注入的 MP REST 客户端和注册的 OidcClient 过滤器来调用下游端点。此端点会将令牌回显回来。例如,请参见 `integration-tests/oidc-client-wiremock`中的 `main`Quarkus 存储库。
例如,设置 application.properties
:
# Use the 'keycloak.url' property set by the test KeycloakRealmResourceManager
quarkus.oidc-client.auth-server-url=${keycloak.url:replaced-by-test-resource}
quarkus.oidc-client.discovery-enabled=false
quarkus.oidc-client.token-path=/tokens
quarkus.oidc-client.client-id=quarkus-service-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=password
quarkus.oidc-client.grant-options.password.username=alice
quarkus.oidc-client.grant-options.password.password=alice
最后,编写测试代码。鉴于上面的基于 Wiremock 的资源,第一个测试调用应返回 `access_token_1`访问令牌,该令牌将在 4 秒后过期。使用 `awaitility`等待大约 5 秒,现在下一个测试调用应返回 `access_token_2`访问令牌,这将确认已刷新过期的 `access_token_1`访问令牌。
Keycloak
如果你要使用 Keycloak,可以使用 OpenID Connect Bearer Token Integration testingKeycloak 部分中描述的相同方法。
How to check the errors in the logs
启用 `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
启用 `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
OIDC request filters
你可以通过注册一个或多个 `OidcRequestFilter`实施来过滤 Quarkus 对 OIDC 提供程序发出的 OIDC 请求,这些实施可以更新或添加新的请求标头。例如,过滤器可以分析请求正文并将摘要作为新标头值添加到其中:
package io.quarkus.it.keycloak;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.common.OidcRequestContextProperties;
import io.quarkus.oidc.common.OidcRequestFilter;
import io.vertx.core.http.HttpMethod;
import io.vertx.mutiny.core.buffer.Buffer;
import io.vertx.mutiny.ext.web.client.HttpRequest;
@ApplicationScoped
@Unremovable
public class OidcRequestCustomizer implements OidcRequestFilter {
@Override
public void filter(HttpRequest<Buffer> request, Buffer buffer, OidcRequestContextProperties contextProperties) {
HttpMethod method = request.method();
String uri = request.uri();
if (method == HttpMethod.POST && uri.endsWith("/service") && buffer != null) {
request.putHeader("Digest", calculateDigest(buffer.toString()));
}
}
private String calculateDigest(String bodyString) {
// Apply the required digest algorithm to the body string
}
}
Token Propagation Reactive
quarkus-rest-client-oidc-token-propagation
扩展提供了一个 REST 客户端 io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter
,它简化了身份验证信息的传播。此客户端传播当前活动请求中存在的 bearer token 或从 authorization code flow mechanism 获取的令牌,作为 HTTP Authorization
标头中的 Bearer
方案值。
你可以使用 io.quarkus.oidc.token.propagation.AccessToken
或 org.eclipse.microprofile.rest.client.annotation.RegisterProvider
注释有选择地注册 AccessTokenRequestReactiveFilter
,例如:
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.AccessToken;
@RegisterRestClient
@AccessToken
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
或
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter;
@RegisterRestClient
@RegisterProvider(AccessTokenRequestReactiveFilter.class)
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
此外,AccessTokenRequestReactiveFilter
可以支持需要在传播代币前交换代币的复杂应用程序。
如果你使用 Keycloak 或其他支持 Token Exchange 令牌授予的 OIDC 提供商,那么你可以像这样配置 AccessTokenRequestReactiveFilter
来交换令牌:
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=exchange
quarkus.oidc-client.grant-options.exchange.audience=quarkus-app-exchange
quarkus.resteasy-client-oidc-token-propagation.exchange-token=true 1
1 | 请注意,当使用 io.quarkus.oidc.token.propagation.AccessToken#exchangeTokenClient 注释属性设置 OidcClient 名称时,将忽略 exchange-token 配置属性。 |
注意 AccessTokenRequestReactiveFilter
将使用 OidcClient
交换当前令牌,而且你可以使用 quarkus.oidc-client.grant-options.exchange
设置你的 OpenID Connect 提供商期望的附加交换属性。
如果你使用 Azure
等提供商,它们 require using JWT bearer token grant 交换当前令牌,那么你可以像这样配置 AccessTokenRequestReactiveFilter
来交换令牌:
quarkus.oidc-client.auth-server-url=${azure.provider.url}
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=jwt
quarkus.oidc-client.grant-options.jwt.requested_token_use=on_behalf_of
quarkus.oidc-client.scopes=https://graph.microsoft.com/user.read,offline_access
quarkus.resteasy-client-oidc-token-propagation.exchange-token=true
AccessTokenRequestReactiveFilter
默认情况下使用默认 OidcClient
。可以使用 quarkus.rest-client-oidc-token-propagation.client-name
配置属性或 io.quarkus.oidc.token.propagation.AccessToken#exchangeTokenClient
注释属性来选择一个命名的 OidcClient
。
Token Propagation
quarkus-resteasy-client-oidc-token-propagation
扩展提供了两个 Jakarta REST jakarta.ws.rs.client.ClientRequestFilter
类实现,它们简化了身份验证信息传播。io.quarkus.oidc.token.propagation.AccessTokenRequestFilter
传播当前活动请求中存在的 Bearer token 或从 Authorization code flow mechanism 获取的令牌,作为 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
还是直接来自 Client
。为了对 Service B
验证令牌来自 Service A
,它应该能够断言新的发布者和受众声明。
此外,复杂应用程序可能需要在传播代币之前交换或更新令牌。例如,当 Service A
访问 Service B
时,访问上下文可能不同。在这种情况下,Service A
可能会得到一个范围较窄或完全不同的范围集来访问 Service B
。
以下部分展示了 AccessTokenRequestFilter
和 JsonWebTokenRequestFilter
如何提供帮助。
RestClient AccessTokenRequestFilter
AccessTokenRequestFilter
将所有令牌视为字符串,因此它可以同时使用 JWT 和不透明令牌。
你可以使用 io.quarkus.oidc.token.propagation.AccessToken
或 org.eclipse.microprofile.rest.client.annotation.RegisterProvider
,例如选择注册 AccessTokenRequestFilter
:
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.AccessToken;
@RegisterRestClient
@AccessToken
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
或
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.AccessTokenRequestFilter;
@RegisterRestClient
@RegisterProvider(AccessTokenRequestFilter.class)
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
或者,如果 quarkus.resteasy-client-oidc-token-propagation.register-filter
属性设置为 true
并且 quarkus.resteasy-client-oidc-token-propagation.json-web-token
属性设置为 false
(这是默认值),则 AccessTokenRequestFilter
可以在所有 MP Rest 或 Jakarta REST 客户端自动注册。
Exchange token before propagation
如果当前访问令牌需要在传播之前交换,并且你使用支持 Token Exchange 令牌授予的 Keycloak 或其他 OpenID Connect 提供商,那么你可以像这样配置 AccessTokenRequestFilter
:
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=exchange
quarkus.oidc-client.grant-options.exchange.audience=quarkus-app-exchange
quarkus.resteasy-client-oidc-token-propagation.exchange-token=true
如果你使用 Azure
等提供商,它们 require using JWT bearer token grant 交换当前令牌,那么你可以像这样配置 AccessTokenRequestFilter
来交换令牌:
quarkus.oidc-client.auth-server-url=${azure.provider.url}
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=jwt
quarkus.oidc-client.grant-options.jwt.requested_token_use=on_behalf_of
quarkus.oidc-client.scopes=https://graph.microsoft.com/user.read,offline_access
quarkus.resteasy-client-oidc-token-propagation.exchange-token=true
注意 AccessTokenRequestFilter
将使用 OidcClient
交换当前令牌,而且你可以使用 quarkus.oidc-client.grant-options.exchange
设置你的 OpenID Connect 提供商期望的附加交换属性。
AccessTokenRequestFilter`默认情况下使用默认`OidcClient
。可以使用`quarkus.resteasy-client-oidc-token-propagation.client-name`配置属性选择具有名称的`OidcClient`。
RestClient JsonWebTokenRequestFilter
如果你使用 Bearer JWT 令牌,并且这些令牌可以修改其声明(如`issuer`和`audience`),并再次保护更新后的令牌(例如重新签名),则建议使用`JsonWebTokenRequestFilter`。它需要注入的`org.eclipse.microprofile.jwt.JsonWebToken`,因此无法与不透明的令牌一起使用。此外,如果你的 OpenID Connect 提供商支持令牌交换协议,那么建议改用`AccessTokenRequestFilter` - 因为两者都可以安全地使用`AccessTokenRequestFilter`交换 JWT 和不透明的持有者令牌。
JsonWebTokenRequestFilter`使`Service A`实现能够轻松地使用新的`issuer`和`audience`声明值更新注入的`org.eclipse.microprofile.jwt.JsonWebToken
,并使用新签名再次保护更新后的令牌。唯一困难的步骤是确保`Service A`具有签名密钥;它应从安全文件系统或 Vault 等远程安全存储进行预配。
你可以使用 io.quarkus.oidc.token.propagation.JsonWebToken`或 `org.eclipse.microprofile.rest.client.annotation.RegisterProvider
,有选择地注册 JsonWebTokenRequestFilter
,例如:
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.JsonWebToken;
@RegisterRestClient
@JsonWebToken
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
或
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.JsonWebTokenRequestFilter;
@RegisterRestClient
@RegisterProvider(JsonWebTokenRequestFilter.class)
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
或者,如果 quarkus.resteasy-client-oidc-token-propagation.register-filter`和 `quarkus.resteasy-client-oidc-token-propagation.json-web-token`属性都设置为 `true
,则可以自动使用所有 MicroProfile REST 或 Jakarta REST 客户端注册 JsonWebTokenRequestFilter
。
Update token before propagation
如果注入令牌需要使用新签名更新并再次保护其`iss`(颁发者)或`aud`(受众)声明,则可以像这样配置`JsonWebTokenRequestFilter`:
quarkus.resteasy-client-oidc-token-propagation.secure-json-web-token=true
smallrye.jwt.sign.key.location=/privateKey.pem
# Set a new issuer
smallrye.jwt.new-token.issuer=http://frontend-resource
# Set a new audience
smallrye.jwt.new-token.audience=http://downstream-resource
# Override the existing token issuer and audience claims if they are already set
smallrye.jwt.new-token.override-matching-claims=true
如前所述,如果你使用 Keycloak 或支持令牌交换协议的 OpenID Connect 提供商,则使用`AccessTokenRequestFilter`。
Testing
你可以按照 OpenID Connect Bearer Token Integration testing部分中的说明生成令牌。准备 REST 测试端点。你可以使用注册的令牌传播过滤器注入 MP REST 客户端的测试前端端点,调用下游端点。例如,请查看 main`Quarkus 存储库中的 `integration-tests/resteasy-client-oidc-token-propagation
。
Token Propagation Reactive
添加以下 Maven 依赖项:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-resteasy-client-oidc-token-propagation</artifactId>
</dependency>
quarkus-rest-client-resteasy-client-oidc-token-propagation`扩展提供 `io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter
,可用于传播当前`Bearer`或 `Authorization Code Flow`访问令牌。
`quarkus-rest-client-resteasy-client-oidc-token-propagation`扩展(与非反应式 `quarkus-resteasy-client-oidc-token-propagation`扩展相对)当前不支持在传播之前交换或重新签名令牌。但是,这些功能可能会在将来添加。
GraphQL client integration
`quarkus-oidc-client-graphql`扩展提供了一种将 OIDC 客户端集成到 GraphQL clients的方法,这与 REST 客户端使用的方法类似。当此扩展处于活动状态时,通过属性(而不是通过构建器以编程方式)配置的任何 GraphQL 客户端都将使用 OIDC 客户端获取访问令牌,然后将其设置为`Authorization`标头值。OIDC 客户端还将刷新过期的访问令牌。
要配置由 GraphQL 客户端使用哪个 OIDC 客户端,请使用 `quarkus.oidc-client-graphql.client-name`属性选择一个已配置的 OIDC 客户端,例如:
quarkus.oidc-client-graphql.client-name=oidc-client-for-graphql # example declaration of the OIDC client itself quarkus.oidc-client.oidc-client-for-graphql.auth-server-url=${keycloak.url} quarkus.oidc-client.oidc-client-for-graphql.grant.type=password quarkus.oidc-client.oidc-client-for-graphql.grant-options.password.username=${username} quarkus.oidc-client.oidc-client-for-graphql.grant-options.password.password=${password} quarkus.oidc-client.oidc-client-for-graphql.client-id=${quarkus.oidc.client-id} quarkus.oidc-client.oidc-client-for-graphql.credentials.client-secret.value=${keycloak.credentials.secret} quarkus.oidc-client.oidc-client-for-graphql.credentials.client-secret.method=POST
如果你未指定 `quarkus.oidc-client-graphql.client-name`属性,GraphQL 客户端将使用默认 OIDC 客户端(无显式名称)。 |
具体针对类型安全 GraphQL 客户端,你可以通过使用 `@io.quarkus.oidc.client.filter.OidcClientFilter`注释 `GraphQLClientApi`接口在逐个客户端的基础上覆盖此设置。例如:
@GraphQLClientApi(configKey = "order-client")
@OidcClientFilter("oidc-client-for-graphql")
public interface OrdersGraphQLClient {
// Queries, mutations, and subscriptions go here.
}
为了能够使用编程方式创建的 GraphQL 客户端,这两个构建器(VertxDynamicGraphQLClientBuilder`和 `VertxTypesafeGraphQLClientBuilder
)都包含一个方法 dynamicHeader(String,
Uni<String>
),允许你插入可能针对每个请求而更改的标头。若要插入 OIDC 客户端,请使用
@Inject
OidcClients oidcClients;
VertxTypesafeGraphQLClientBuilder builder = ....;
Uni<String> tokenUni = oidcClients.getClient("OIDC_CLIENT_NAME")
.getTokens().map(t -> "Bearer " + t.getAccessToken());
builder.dynamicHeader("Authorization", tokenUni);
VertxDynamicGraphQLClient client = builder.build();