OpenID Connect client and token propagation quickstart
了解如何在应用程序中使用 OpenID Connect (OIDC) 和 OAuth2 客户端以及过滤器来获取、刷新和传播访问令牌。
Learn how to use OpenID Connect (OIDC) and OAuth2 clients with filters to get, refresh, and propagate access tokens in your applications.
有关 Quarkus 中 OIDC Client
和 Token Propagation
支持的更多信息,请参阅 OpenID Connect (OIDC) and OAuth2 client and filters reference guide。
For more information about OIDC Client
and Token Propagation
support in Quarkus, see the OpenID Connect (OIDC) and OAuth2 client and filters reference guide.
若要使用带有人员令牌授权来保护应用程序,请参阅 OpenID Connect (OIDC) Bearer token authentication 指南。
To protect your applications by using Bearer Token Authorization, see the OpenID Connect (OIDC) Bearer token authentication guide.
Architecture
在此示例中,应用程序是使用两个 Jakarta REST 资源,FrontendResource
和 ProtectedResource`构建的。此处,`FrontendResource
使用以下三种方法之一将访问令牌传播至 ProtectedResource
:
In this example, an application is built with two Jakarta REST resources, FrontendResource
and ProtectedResource
.
Here, FrontendResource
uses one of three methods to propagate access tokens to ProtectedResource
:
-
It can get a token by using an OIDC client filter before propagating it.
-
It can get a token by using a programmatically created OIDC client and propagate it by passing it to a REST client method as an HTTP
Authorization
header value. -
It can use an OIDC token propagation filter to propagate the incoming access token.
FrontendResource
有 8 个端点:
FrontendResource
has eight endpoints:
-
/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
端点时,FrontendResource
使用具有 OIDC 客户端过滤器的 REST 客户端获取访问令牌并将其传播到 ProtectedResource
。当调用 /frontend/user-name-with-oidc-client-token-header-param
或 /frontend/admin-name-with-oidc-client-token-header-param
端点时,FrontendResource
使用 以编程方式创建的 OIDC 客户端获取访问令牌并将其传播到 ProtectedResource
,方法是将其作为 HTTP Authorization
标头值传递给 REST 客户端方法。当调用 /frontend/user-name-with-propagated-token
或 /frontend/admin-name-with-propagated-token
端点时,FrontendResource
使用带有 OIDC Token Propagation Filter
的 REST 客户端将当前传入的访问令牌传播到 ProtectedResource
。
When either /frontend/user-name-with-oidc-client-token
or /frontend/admin-name-with-oidc-client-token
endpoint is called, FrontendResource
uses a REST client with an OIDC client filter to get and propagate an access token to ProtectedResource
.
When either /frontend/user-name-with-oidc-client-token-header-param
or /frontend/admin-name-with-oidc-client-token-header-param
endpoint is called, FrontendResource
uses a programmatically created OIDC client to get and propagate an access token to ProtectedResource
by passing it to a REST client method as an HTTP Authorization
header value.
When either /frontend/user-name-with-propagated-token
or /frontend/admin-name-with-propagated-token
endpoint is called, FrontendResource
uses a REST client with OIDC Token Propagation Filter
to propagate the current incoming access token to ProtectedResource
.
ProtectedResource
有两个端点:
ProtectedResource
has two endpoints:
-
/protected/user-name
-
/protected/admin-name
两个端点返回从传入访问令牌中提取的用户名,该用户名已从 ProtectedResource
传播到 FrontendResource
。这些端点之间的唯一区别在于,仅当当前访问令牌具有 user
角色时才允许调用 /protected/user-name
,并且仅当当前访问令牌具有 admin
角色时才允许调用 /protected/admin-name
。
Both endpoints return the username extracted from the incoming access token, which was propagated to ProtectedResource
from FrontendResource
.
The only difference between these endpoints is that calling /protected/user-name
is only allowed if the current access token has a user
role, and calling /protected/admin-name
is only allowed if the current access token has an admin
role.
Solution
我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。
We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.
克隆 Git 存储库: git clone {quickstarts-clone-url}
,或下载 {quickstarts-archive-url}[存档]。
Clone the Git repository: git clone {quickstarts-clone-url}
, or download an {quickstarts-archive-url}[archive].
此解决方案位于 security-openid-connect-client-quickstart
directory。
The solution is in the security-openid-connect-client-quickstart
directory.
Creating the Maven project
首先,你需要一个新项目。使用以下命令创建一个新项目:
First, you need a new project. Create a new project with the following command:
Unresolved directive in security-openid-connect-client.adoc - include::{includes}/devtools/create-app.adoc[]
它会生成一个 Maven 项目,导入 oidc
、rest-client-oidc-filter
、rest-client-oidc-token-propagation
和 rest
扩展。
It generates a Maven project, importing the oidc
, rest-client-oidc-filter
, rest-client-oidc-token-propagation
, and rest
extensions.
如果您已经配置了 Quarkus 项目,可以通过在项目根目录中运行以下命令将这些扩展添加到您的项目:
If you already have your Quarkus project configured, you can add these extensions to your project by running the following command in your project base directory:
Unresolved directive in security-openid-connect-client.adoc - include::{includes}/devtools/extension-add.adoc[]
它会将以下扩展添加到您的构建文件中:
It adds the following extensions to your build file:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-oidc-filter</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-oidc-token-propagation</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
</dependency>
implementation("io.quarkus:quarkus-oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest")
Writing the application
首先实现 ProtectedResource
:
Start by implementing ProtectedResource
:
package org.acme.security.openid.connect.client;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import io.quarkus.security.Authenticated;
import io.smallrye.mutiny.Uni;
import org.eclipse.microprofile.jwt.JsonWebToken;
@Path("/protected")
@Authenticated
public class ProtectedResource {
@Inject
JsonWebToken principal;
@GET
@RolesAllowed("user")
@Produces("text/plain")
@Path("userName")
public Uni<String> userName() {
return Uni.createFrom().item(principal.getName());
}
@GET
@RolesAllowed("admin")
@Produces("text/plain")
@Path("adminName")
public Uni<String> adminName() {
return Uni.createFrom().item(principal.getName());
}
}
ProtectedResource
从 userName()
和 adminName()
方法返回用户名。用户名从当前 JsonWebToken
中提取。
ProtectedResource
returns a name from both userName()
and adminName()
methods.
The name is extracted from the current JsonWebToken
.
接下来,添加以下 REST 客户端:
Next, add the following REST clients:
-
RestClientWithOidcClientFilter
, which uses an OIDC client filter provided by thequarkus-rest-client-oidc-filter
extension to get and propagate an access token. -
RestClientWithTokenHeaderParam
, which accepts a token already acquired by the programmatically created OidcClient as an HTTPAuthorization
header value. -
RestClientWithTokenPropagationFilter
, which uses an OIDC token propagation filter provided by thequarkus-rest-client-oidc-token-propagation
extension to get and propagate an access token.
添加 RestClientWithOidcClientFilter
REST 客户端:
Add the RestClientWithOidcClientFilter
REST client:
package org.acme.security.openid.connect.client;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
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 1
@Path("/")
public interface RestClientWithOidcClientFilter {
@GET
@Produces("text/plain")
@Path("userName")
Uni<String> getUserName();
@GET
@Produces("text/plain")
@Path("adminName")
Uni<String> getAdminName();
}
1 | Register an OIDC client filter with the REST client to get and propagate the tokens. |
添加 RestClientWithTokenHeaderParam
REST 客户端:
Add the RestClientWithTokenHeaderParam
REST client:
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); 1
@GET
@Produces("text/plain")
@Path("adminName")
Uni<String> getAdminName(@HeaderParam("Authorization") String authorization); 1
}
1 | RestClientWithTokenHeaderParam REST client expects that the tokens will be passed to it as HTTP Authorization header values. |
添加 RestClientWithTokenPropagationFilter
REST 客户端:
Add the RestClientWithTokenPropagationFilter
REST client:
package org.acme.security.openid.connect.client;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.AccessToken;
import io.smallrye.mutiny.Uni;
@RegisterRestClient
@AccessToken 1
@Path("/")
public interface RestClientWithTokenPropagationFilter {
@GET
@Produces("text/plain")
@Path("userName")
Uni<String> getUserName();
@GET
@Produces("text/plain")
@Path("adminName")
Uni<String> getAdminName();
}
1 | Register an OIDC token propagation filter with the REST client to propagate the incoming already-existing tokens. |
不要在同一 REST 客户端中使用 RestClientWithOidcClientFilter
和 RestClientWithTokenPropagationFilter
接口,因为它们可能会冲突,从而导致问题。例如,OIDC 客户端过滤器可以覆盖来自 OIDC 令牌传播过滤器的令牌,或者传播过滤器在尝试传播令牌而令牌不可用时可能无法正确工作,而是希望 OIDC 客户端过滤器获取新的令牌。
Do not use the RestClientWithOidcClientFilter
and RestClientWithTokenPropagationFilter
interfaces in the same REST client because they can conflict, leading to issues.
For example, the OIDC client filter can override the token from the OIDC token propagation filter, or the propagation filter might not work correctly if it attempts to propagate a token when none is available, expecting the OIDC client filter to obtain a new token instead.
另外,添加 OidcClientCreator
以在启动时以编程方式创建一个 OIDC 客户端。OidcClientCreator
支持 RestClientWithTokenHeaderParam
REST 客户端调用:
Also, add OidcClientCreator
to create an OIDC client programmatically at startup. OidcClientCreator
supports RestClientWithTokenHeaderParam
REST client calls:
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; 1
@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);
}
}
1 | OidcClients can be used to retrieve the already initialized, named OIDC clients and create new OIDC clients on demand. |
现在,通过添加 FrontendResource
来完成应用程序的创建:
Now, finish creating the application by adding FrontendResource
:
package org.acme.security.openid.connect.client;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import io.quarkus.oidc.client.Tokens;
import io.quarkus.oidc.client.runtime.TokensHelper;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import io.smallrye.mutiny.Uni;
@Path("/frontend")
public class FrontendResource {
@Inject
@RestClient
RestClientWithOidcClientFilter restClientWithOidcClientFilter; 1
@Inject
@RestClient
RestClientWithTokenPropagationFilter restClientWithTokenPropagationFilter; 2
@Inject
OidcClientCreator oidcClientCreator;
TokensHelper tokenHelper = new TokensHelper(); 5
@Inject
@RestClient
RestClientWithTokenHeaderParam restClientWithTokenHeaderParam; 3
@GET
@Path("user-name-with-oidc-client-token")
@Produces("text/plain")
public Uni<String> getUserNameWithOidcClientToken() { 1
return restClientWithOidcClientFilter.getUserName();
}
@GET
@Path("admin-name-with-oidc-client-token")
@Produces("text/plain")
public Uni<String> getAdminNameWithOidcClientToken() { 1
return restClientWithOidcClientFilter.getAdminName();
}
@GET
@Path("user-name-with-propagated-token")
@Produces("text/plain")
public Uni<String> getUserNameWithPropagatedToken() { 2
return restClientWithTokenPropagationFilter.getUserName();
}
@GET
@Path("admin-name-with-propagated-token")
@Produces("text/plain")
public Uni<String> getAdminNameWithPropagatedToken() { 2
return restClientWithTokenPropagationFilter.getAdminName();
}
@GET
@Path("user-name-with-oidc-client-token-header-param")
@Produces("text/plain")
public Uni<String> getUserNameWithOidcClientTokenHeaderParam() { 3
return tokenHelper.getTokens(oidcClientCreator.getOidcClient()).onItem()
.transformToUni(tokens -> restClientWithTokenHeaderParam.getUserName("Bearer " + tokens.getAccessToken()));
}
@GET
@Path("admin-name-with-oidc-client-token-header-param")
@Produces("text/plain")
public Uni<String> getAdminNameWithOidcClientTokenHeaderParam() { 3
return tokenHelper.getTokens(oidcClientCreator.getOidcClient()).onItem()
.transformToUni(tokens -> restClientWithTokenHeaderParam.getAdminName("Bearer " + tokens.getAccessToken()));
}
@GET
@Path("user-name-with-oidc-client-token-header-param-blocking")
@Produces("text/plain")
public String getUserNameWithOidcClientTokenHeaderParamBlocking() { 4
Tokens tokens = tokenHelper.getTokens(oidcClientCreator.getOidcClient()).await().indefinitely();
return restClientWithTokenHeaderParam.getUserName("Bearer " + tokens.getAccessToken()).await().indefinitely();
}
@GET
@Path("admin-name-with-oidc-client-token-header-param-blocking")
@Produces("text/plain")
public String getAdminNameWithOidcClientTokenHeaderParamBlocking() { 4
Tokens tokens = tokenHelper.getTokens(oidcClientCreator.getOidcClient()).await().indefinitely();
return restClientWithTokenHeaderParam.getAdminName("Bearer " + tokens.getAccessToken()).await().indefinitely();
}
}
1 | FrontendResource uses the injected RestClientWithOidcClientFilter REST client with the OIDC client filter to get and propagate an access token to ProtectedResource when either /frontend/user-name-with-oidc-client-token or /frontend/admin-name-with-oidc-client-token is called. |
2 | FrontendResource uses the injected RestClientWithTokenPropagationFilter REST client with the OIDC token propagation filter to propagate the current incoming access token to ProtectedResource when either /frontend/user-name-with-propagated-token or /frontend/admin-name-with-propagated-token is called. |
3 | FrontendResource uses the programmatically created OIDC client to get and propagate an access token to ProtectedResource by passing it directly to the injected RestClientWithTokenHeaderParam REST client’s method as an HTTP Authorization header value, when either /frontend/user-name-with-oidc-client-token-header-param or /frontend/admin-name-with-oidc-client-token-header-param is called. |
4 | Sometimes, one may have to acquire tokens in a blocking manner before propagating them with the REST client. This example shows how to acquire the tokens in such cases. |
5 | io.quarkus.oidc.client.runtime.TokensHelper is a useful tool when OIDC client is used directly, without the OIDC client filter. To use TokensHelper , pass OIDC Client to it to get the tokens and TokensHelper acquires the tokens and refreshes them if necessary in a thread-safe way. |
最后,添加一个 Jakarta REST ExceptionMapper
:
Finally, add a Jakarta REST ExceptionMapper
:
package org.acme.security.openid.connect.client;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import org.jboss.resteasy.reactive.ClientWebApplicationException;
@Provider
public class FrontendExceptionMapper implements ExceptionMapper<ClientWebApplicationException> {
@Override
public Response toResponse(ClientWebApplicationException t) {
return Response.status(t.getResponse().getStatus()).build();
}
}
仅添加此异常映射程序以在测试期间验证当令牌没有预期角色时,ProtectedResource
是否返回 403
。如果没有此映射程序,Quarkus REST (formerly RESTEasy Reactive)
将正确地将从 REST 客户端调用逃逸的异常转换为 500
,以避免泄露诸如 ProtectedResource
等下游资源的信息。然而,在测试中,将不可能断言 500
是由授权异常而不是某些内部错误引起的。
This exception mapper is only added to verify during the tests that ProtectedResource
returns 403
when the token has no expected role.
Without this mapper, Quarkus REST (formerly RESTEasy Reactive)
would correctly convert the exceptions that escape from REST client calls to 500
to avoid leaking the information from the downstream resources such as ProtectedResource
.
However, in the tests, it would not be possible to assert that 500
is caused by an authorization exception instead of some internal error.
Configuring the application
在准备了代码后,请配置应用程序:
Having prepared the code, you configure the application:
# Configure OIDC
%prod.quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=backend-service
quarkus.oidc.credentials.secret=secret
# Tell Dev Services for Keycloak to import the realm file
# This property is ineffective when running the application in JVM or Native modes but only in dev and test modes.
quarkus.keycloak.devservices.realm-path=quarkus-realm.json
# Configure OIDC Client
quarkus.oidc-client.auth-server-url=${quarkus.oidc.auth-server-url}
quarkus.oidc-client.client-id=${quarkus.oidc.client-id}
quarkus.oidc-client.credentials.secret=${quarkus.oidc.credentials.secret}
quarkus.oidc-client.grant.type=password
quarkus.oidc-client.grant-options.password.username=alice
quarkus.oidc-client.grant-options.password.password=alice
# Configure REST clients
%prod.port=8080
%dev.port=8080
%test.port=8081
org.acme.security.openid.connect.client.RestClientWithOidcClientFilter/mp-rest/url=http://localhost:${port}/protected
org.acme.security.openid.connect.client.RestClientWithTokenHeaderParam/mp-rest/url=http://localhost:${port}/protected
org.acme.security.openid.connect.client.RestClientWithTokenPropagationFilter/mp-rest/url=http://localhost:${port}/protected
前面的配置引用了 Keycloak,其中由 ProtectedResource
来验证传入的访问令牌,由 OidcClient
来使用 password
授权获取用户 alice
的令牌。两个 REST 客户端都指向 “ProtectedResource” 的 HTTP 地址。
The preceding configuration references Keycloak, which is used by ProtectedResource
to verify the incoming access tokens and by OidcClient
to get the tokens for a user alice
by using a password
grant.
Both REST clients point to `ProtectedResource’s HTTP address.
将 |
Adding a |
Starting and configuring the Keycloak server
在以开发或测试模式运行应用程序时,不要启动 Keycloak 服务器; |
Do not start the Keycloak server when you run the application in dev or test modes; |
若要启动 Keycloak 服务器,可以使用 Docker 并只需运行以下命令:
To start a Keycloak Server, you can use Docker and just run the following command:
docker run --name keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8180:8080 quay.io/keycloak/keycloak:{keycloak.version} start-dev
将 {keycloak.version}
设置为 25.0.2
或更高版本。
Set {keycloak.version}
to 25.0.2
or later.
您可以在 localhost:8180 访问您的 Keycloak 服务器。
You can access your Keycloak Server at localhost:8180.
以 admin
用户身份登录以访问 Keycloak 管理控制台。密码是 admin
。
Log in as the admin
user to access the Keycloak Administration Console.
The password is admin
.
导入 realm configuration file 以创建新领域。有关详细信息,请参阅 Keycloak 文档,了解如何 create a new realm。
Import the realm configuration file to create a new realm. For more details, see the Keycloak documentation about how to create a new realm.
此 quarkus
领域文件添加了一个 frontend
客户端和 alice
和 admin
用户。 alice
有一个 user
角色。 admin
有 user
和 admin
两个角色。
This quarkus
realm file adds a frontend
client, and alice
and admin
users.
alice
has a user
role.
admin
has both user
and admin
roles.
Running the application in dev mode
若要以开发人员模式运行该应用程序,请使用:
To run the application in a dev mode, use:
Unresolved directive in security-openid-connect-client.adoc - include::{includes}/devtools/dev.adoc[]
Dev Services for Keycloak 启动 Keycloak 容器并导入 quarkus-realm.json
。
Dev Services for Keycloak launches a Keycloak container and imports quarkus-realm.json
.
Open a Dev UI available at /q/dev-ui and click a Provider: Keycloak
link in the OpenID Connect Dev UI card.
系统提示时,登录到 OpenID Connect Dev UI 提供的 Single Page Application
:
When asked, log in to a Single Page Application
provided by the OpenID Connect Dev UI:
-
Log in as
alice
, with the password,alice
. This user has auser
role.-
Access
/frontend/user-name-with-propagated-token
, which returns200
. -
Access
/frontend/admin-name-with-propagated-token
, which returns403
.
-
-
Log out and back in as
admin
with the password,admin
. This user has bothadmin
anduser
roles.-
Access
/frontend/user-name-with-propagated-token
, which returns200
. -
Access
/frontend/admin-name-with-propagated-token
, which returns200
.
-
您已经测试过 FrontendResource
可以从 OpenID Connect Dev UI 传播访问令牌。
You have tested that FrontendResource
can propagate the access tokens from the OpenID Connect Dev UI.
Running the application in JVM mode
在 dev 模式下探索应用程序后,您可以将其作为标准 Java 应用程序运行。
After exploring the application in dev mode, you can run it as a standard Java application.
首先,对其进行编译:
First, compile it:
Unresolved directive in security-openid-connect-client.adoc - include::{includes}/devtools/build.adoc[]
然后,运行它:
Then, run it:
java -jar target/quarkus-app/quarkus-run.jar
Running the application in native mode
你可以将此演示编译为本地代码;不需要任何修改。
You can compile this demo into native code; no modifications are required.
这意味着你不再需要在你的生产环境中安装 JVM,因为运行时技术包含在生成的可执行文件中并经过优化,以便在最小资源下运行。
This implies that you no longer need to install a JVM on your production environment, as the runtime technology is included in the produced binary and optimized to run with minimal resources.
编译时间较长,因此此步骤默认关闭。要再次构建,请启用 native
配置文件:
Compilation takes longer, so this step is turned off by default.
To build again, enable the native
profile:
Unresolved directive in security-openid-connect-client.adoc - include::{includes}/devtools/build-native.adoc[]
一段时间后,构建完成后,你可以直接运行本地可执行文件:
After a little while, when the build finishes, you can run the native binary directly:
./target/security-openid-connect-quickstart-1.0.0-SNAPSHOT-runner
Testing the application
有关在开发模式下测试你的应用程序的更多信息,请参阅前面的 Running the application in dev mode 部分。
For more information about testing your application in dev mode, see the preceding oidc-client-keycloak-dev-mode section.
你可以使用 curl
测试在 JVM 或 Native 模式下启动的应用程序。
You can test the application launched in JVM or Native modes with curl
.
获取 alice
的访问令牌:
Obtain an access token for alice
:
export access_token=$(\
curl --insecure -X POST http://localhost:8180/realms/quarkus/protocol/openid-connect/token \
--user backend-service:secret \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'username=alice&password=alice&grant_type=password' | jq --raw-output '.access_token' \
)
使用此令牌调用 /frontend/user-name-with-propagated-token
。此命令返回 200
状态码和名称 alice
:
Use this token to call /frontend/user-name-with-propagated-token
. This command returns the 200
status code and the name alice
:
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
仅有 user
角色:
Use the same token to call /frontend/admin-name-with-propagated-token
. In contrast to the preceding command, this command returns 403
because alice
has only a user
role:
curl -i -X GET \
http://localhost:8080/frontend/admin-name-with-propagated-token \
-H "Authorization: Bearer "$access_token
接下来,获取 admin
的访问令牌:
Next, obtain an access token for admin
:
export access_token=$(\
curl --insecure -X POST http://localhost:8180/realms/quarkus/protocol/openid-connect/token \
--user backend-service:secret \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'username=admin&password=admin&grant_type=password' | jq --raw-output '.access_token' \
)
使用此令牌调用 /frontend/user-name-with-propagated-token
。此命令返回 200
状态码和名称 admin
:
Use this token to call /frontend/user-name-with-propagated-token
. This command returns a 200
status code and the name admin
:
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
角色:
Use the same token to call /frontend/admin-name-with-propagated-token
. This command also returns the 200
status code and the name admin
because admin
has both user
and admin
roles:
curl -i -X GET \
http://localhost:8080/frontend/admin-name-with-propagated-token \
-H "Authorization: Bearer "$access_token
接下来,查看 FrontendResource
方法,该方法不会传播现有令牌,而是使用 OidcClient
来获取并传播令牌。如已展示,OidcClient
被配置为获取 alice
用户的令牌。
Next, check the FrontendResource
methods, which do not propagate the existing tokens but use OidcClient
to get and propagate the tokens.
As already shown, OidcClient
is configured to get the tokens for the alice
user.
curl -i -X GET \
http://localhost:8080/frontend/user-name-with-oidc-client-token
此命令返回 200
状态码和名称 alice
。
This command returns the 200
status code and the name alice
.
curl -i -X GET \
http://localhost:8080/frontend/admin-name-with-oidc-client-token
与此前命令相反,此命令返回 403
状态码。
In contrast with the preceding command, this command returns a 403
status code.
接下来,测试以编程方式创建的 OIDC 客户端是否在响应式和命令式(阻塞)模式中正确获取并传播令牌。
Next, test that the programmatically created OIDC client correctly acquires and propagates the token with RestClientWithTokenHeaderParam
both in reactive and imperative (blocking) modes.
调用 /user-name-with-oidc-client-token-header-param
。此命令返回 200
状态码和名称 alice
:
Call the /user-name-with-oidc-client-token-header-param
. This command returns the 200
status code and the name alice
:
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
状态码:
Call the /admin-name-with-oidc-client-token-header-param
. In contrast with the preceding command, this command returns a 403
status code:
curl -i -X GET \
http://localhost:8080/frontend/admin-name-with-oidc-client-token-param
接下来,测试在阻塞模式下使用 OIDC 客户端的端点。
Next, test the endpoints which use OIDC client in in the blocking mode.
调用 /user-name-with-oidc-client-token-header-param-blocking
。此命令返回 200`状态代码和名称 `alice
:
Call the /user-name-with-oidc-client-token-header-param-blocking
. This command returns the 200
status code and the name alice
:
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`状态代码:
Call the /admin-name-with-oidc-client-token-header-param-blocking
. In contrast with the preceding command, this command returns a 403
status code:
curl -i -X GET \
http://localhost:8080/frontend/admin-name-with-oidc-client-token-param-blocking