编辑此页面

OpenID Connect 客户端和令牌传播快速入门

了解如何在您的应用程序中使用带有过滤器的 OpenID Connect (OIDC) 和 OAuth2 客户端来获取、刷新和传播访问令牌。

有关 Quarkus 中 OIDC ClientToken Propagation 支持的更多信息,请参阅 OpenID Connect (OIDC) 和 OAuth2 客户端和过滤器参考指南

要使用 Bearer Token Authorization 保护您的应用程序,请参阅 OpenID Connect (OIDC) Bearer 令牌身份验证 指南。

先决条件

要完成本指南,您需要

  • 大约 15 分钟

  • 一个 IDE

  • 已安装 JDK 17+ 并正确配置了 JAVA_HOME

  • Apache Maven 3.9.9

  • 一个正常工作的容器运行时(Docker 或 Podman

  • 如果您想使用它,可以选择 Quarkus CLI

  • 如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置

  • jq 工具

架构

在此示例中,应用程序构建了两个 Jakarta REST 资源 FrontendResourceProtectedResource。在此,FrontendResource 使用三种方法之一将访问令牌传播到 ProtectedResource

  • 它可以在传播令牌之前使用 OIDC 客户端过滤器获取令牌。

  • 它可以借助以编程方式创建的 OIDC 客户端获取令牌,并通过将令牌作为 HTTP Authorization 标头值传递给 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 端点时,FrontendResource 使用带有 OIDC 客户端过滤器的 REST 客户端来获取访问令牌,并将其传播到 ProtectedResource。当调用 /frontend/user-name-with-oidc-client-token-header-param/frontend/admin-name-with-oidc-client-token-header-param 端点时,FrontendResource 使用以编程方式创建的 OIDC 客户端来获取访问令牌,并通过将令牌作为 HTTP Authorization 标头值传递给 REST 客户端方法来将其传播到 ProtectedResource。当调用 /frontend/user-name-with-propagated-token/frontend/admin-name-with-propagated-token 端点时,FrontendResource 使用带有 OIDC 令牌传播过滤器 的 REST 客户端将当前的传入访问令牌传播到 ProtectedResource

ProtectedResource 有两个端点

  • /protected/user-name

  • /protected/admin-name

这两个端点都返回从传入的访问令牌中提取的用户名,该令牌已从 FrontendResource 传播到 ProtectedResource。这些端点之间的唯一区别是,只有当当前访问令牌具有 user 角色时才允许调用 /protected/user-name,并且只有当当前访问令牌具有 admin 角色时才允许调用 /protected/admin-name

解决方案

我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。

克隆 Git 存储库:git clone https://github.com/quarkusio/quarkus-quickstarts.git,或下载 归档文件

解决方案位于 security-openid-connect-client-quickstart 目录中。

创建 Maven 项目

首先,您需要一个新项目。使用以下命令创建一个新项目

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-quickstart

要创建 Gradle 项目,请添加 --gradle--gradle-kotlin-dsl 选项。

有关如何安装和使用 Quarkus CLI 的更多信息,请参阅 Quarkus CLI 指南。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=security-openid-connect-client-quickstart \
    -Dextensions='oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest' \
    -DnoCode
cd security-openid-connect-client-quickstart

要创建 Gradle 项目,请添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

对于 Windows 用户

  • 如果使用 cmd,(不要使用反斜杠 \ 并将所有内容放在同一行上)

  • 如果使用 Powershell,请将 -D 参数括在双引号中,例如 "-DprojectArtifactId=security-openid-connect-client-quickstart"

它会生成一个 Maven 项目,导入 oidcrest-client-oidc-filterrest-client-oidc-token-propagationrest 扩展。

如果您已经配置了 Quarkus 项目,则可以通过在项目基本目录中运行以下命令,将这些扩展添加到您的项目中

CLI
quarkus extension add oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest
Maven
./mvnw quarkus:add-extension -Dextensions='oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest'
Gradle
./gradlew addExtension --extensions='oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest'

它会将以下扩展添加到您的构建文件中

pom.xml
<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>
build.gradle
implementation("io.quarkus:quarkus-oidc,rest-client-oidc-filter,rest-client-oidc-token-propagation,rest")

编写应用程序

首先实现 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());
    }
}

ProtectedResourceuserName()adminName() 方法中返回名称。该名称是从当前的 JsonWebToken 中提取的。

接下来,添加以下 REST 客户端

  1. RestClientWithOidcClientFilter,它使用 quarkus-rest-client-oidc-filter 扩展提供的 OIDC 客户端过滤器来获取和传播访问令牌。

  2. RestClientWithTokenHeaderParam,它接受已通过编程方式创建的 OidcClient 获取的令牌作为 HTTP Authorization 标头值。

  3. RestClientWithTokenPropagationFilter,它使用 quarkus-rest-client-oidc-token-propagation 扩展提供的 OIDC 令牌传播过滤器来获取和传播访问令牌。

添加 RestClientWithOidcClientFilter REST 客户端

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.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 使用 REST 客户端注册一个 OIDC 客户端过滤器,以获取和传播令牌。

添加 RestClientWithTokenHeaderParam 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); (1)

    @GET
    @Produces("text/plain")
    @Path("adminName")
    Uni<String> getAdminName(@HeaderParam("Authorization") String authorization); (1)
}
1 RestClientWithTokenHeaderParam REST 客户端期望令牌将作为 HTTP Authorization 标头值传递给它。

添加 RestClientWithTokenPropagationFilter REST 客户端

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.inject.RegisterRestClient;

import io.quarkus.oidc.token.propagation.common.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 使用 REST 客户端注册一个 OIDC 令牌传播过滤器,以传播传入的已存在的令牌。
不要在同一个 REST 客户端中使用 RestClientWithOidcClientFilterRestClientWithTokenPropagationFilter 接口,因为它们可能会冲突,从而导致问题。例如,OIDC 客户端过滤器可以覆盖来自 OIDC 令牌传播过滤器的令牌,或者如果传播过滤器尝试传播令牌时没有可用的令牌,则传播过滤器可能无法正常工作,因为它希望 OIDC 客户端过滤器获取新令牌。

另外,添加 OidcClientCreator 以在启动时以编程方式创建 OIDC 客户端。OidcClientCreator 支持 RestClientWithTokenHeaderParam REST 客户端调用

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.OidcClients;
import io.quarkus.oidc.client.runtime.OidcClientConfig;
import io.quarkus.oidc.client.runtime.OidcClientConfig.Grant.Type;
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 = OidcClientConfig
            .authServerUrl(oidcProviderAddress)
            .id("myclient")
            .clientId("backend-service")
            .credentials("secret")
            .grant(Type.PASSWORD)
            .grantOptions("password", Map.of("username", "alice", "password", "alice"))
            .build();
        return oidcClients.newClient(cfg);
    }
}
1 OidcClients 可用于检索已初始化的命名 OIDC 客户端,并按需创建新的 OIDC 客户端。

现在,通过添加 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 当调用 /frontend/user-name-with-oidc-client-token/frontend/admin-name-with-oidc-client-token 时,FrontendResource 使用带有 OIDC 客户端过滤器的注入的 RestClientWithOidcClientFilter REST 客户端来获取访问令牌,并将其传播到 ProtectedResource
2 当调用 /frontend/user-name-with-propagated-token/frontend/admin-name-with-propagated-token 时,FrontendResource 使用带有 OIDC 令牌传播过滤器的注入的 RestClientWithTokenPropagationFilter REST 客户端将当前的传入访问令牌传播到 ProtectedResource
3 当调用 /frontend/user-name-with-oidc-client-token-header-param/frontend/admin-name-with-oidc-client-token-header-param 时,FrontendResource 使用以编程方式创建的 OIDC 客户端来获取访问令牌,并通过将其作为 HTTP Authorization 标头值直接传递给注入的 RestClientWithTokenHeaderParam REST 客户端的方法来将其传播到 ProtectedResource
4 有时,可能需要在以阻塞方式获取令牌,然后再通过 REST 客户端传播令牌。此示例演示了在这种情况下如何获取令牌。
5 当直接使用 OIDC 客户端时,io.quarkus.oidc.client.runtime.TokensHelper 是一个有用的工具,无需 OIDC 客户端过滤器。要使用 TokensHelper,请将 OIDC 客户端传递给它以获取令牌,TokensHelper 会以线程安全的方式获取令牌并在必要时刷新它们。

最后,添加一个 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(以前的 RESTEasy Reactive)会将从 REST 客户端调用中转义的异常正确转换为 500,以避免泄漏来自下游资源(例如 ProtectedResource)的信息。但是,在测试中,无法断言 500 是由授权异常引起的,而不是由某些内部错误引起的。

配置应用程序

准备好代码后,您就可以配置应用程序了

# Configure OIDC

%prod.quarkus.oidc.auth-server-url=https://: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=https://:${port}/protected
org.acme.security.openid.connect.client.RestClientWithTokenHeaderParam/mp-rest/url=https://:${port}/protected
org.acme.security.openid.connect.client.RestClientWithTokenPropagationFilter/mp-rest/url=https://:${port}/protected

前面的配置引用了 Keycloak,ProtectedResource 使用 Keycloak 来验证传入的访问令牌,OidcClient 使用 Keycloak 通过 password 授权为用户 alice 获取令牌。两个 REST 客户端都指向 ProtectedResource 的 HTTP 地址。

quarkus.oidc.auth-server-url 中添加 %prod. 配置文件前缀可确保在开发或测试模式下运行应用程序时,Dev Services for Keycloak 会为您启动一个容器。有关更多信息,请参阅在开发模式下运行应用程序部分。

启动和配置 Keycloak 服务器

在开发或测试模式下运行应用程序时,请勿启动 Keycloak 服务器;Dev Services for Keycloak 会启动一个容器。有关更多信息,请参阅在开发模式下运行应用程序部分。确保将 realm 配置文件 放在类路径上的 target/classes 目录中。此放置可确保该文件在开发模式下自动导入。但是,如果您已经构建了完整解决方案,则无需将 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

{keycloak.version} 设置为 26.2.4 或更高版本。

您可以在 localhost:8180 访问您的 Keycloak 服务器。

admin 用户身份登录以访问 Keycloak 管理控制台。密码是 admin

导入 realm 配置文件 以创建一个新 realm。有关更多详细信息,请参阅 Keycloak 文档中有关如何创建新 realm 的信息。

quarkus realm 文件添加了 frontend 客户端,以及 aliceadmin 用户。alice 具有 user 角色。admin 具有 useradmin 角色。

在开发模式下运行应用程序

要在开发模式下运行应用程序,请使用

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

Dev Services for Keycloak 启动 Keycloak 容器并导入 quarkus-realm.json

打开一个 Dev UI(可在 /q/dev-ui 处获得),然后单击 OpenID Connect Dev UI 卡中的 Keycloak provider 链接。

当出现提示时,登录到 OpenID Connect Dev UI 提供的 Single Page Application

  • admin 身份登录,密码为 admin。此用户具有 adminuser 角色。

    • 访问 /frontend/user-name-with-propagated-token,它会返回 200

    • 访问 /frontend/admin-name-with-propagated-token,它会返回 200

  • 注销并以 alice 身份重新登录,密码为 alice。此用户具有 user 角色。

    • 访问 /frontend/user-name-with-propagated-token,它会返回 200

    • 访问 /frontend/admin-name-with-propagated-token,它会返回 403

您已经测试了 FrontendResource 可以传播来自 OpenID Connect Dev UI 的访问令牌。

在 JVM 模式下运行应用程序

在开发模式下探索完应用程序后,您可以将其作为标准的 Java 应用程序运行。

首先,编译它

CLI
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

然后,运行它

java -jar target/quarkus-app/quarkus-run.jar

在 Native 模式下运行应用程序

您可以将此演示编译为本机代码;无需修改。

这意味着您不再需要在生产环境中安装 JVM,因为运行时技术已包含在生成的二进制文件中,并且经过优化可以以最少的资源运行。

编译需要更长的时间,因此默认情况下此步骤已关闭。要再次构建,请启用 native 配置文件

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

稍等片刻,当构建完成后,您可以直接运行本机二进制文件

./target/security-openid-connect-quickstart-1.0.0-SNAPSHOT-runner

测试应用程序

有关在开发模式下测试应用程序的更多信息,请参阅前面的在开发模式下运行应用程序部分。

您可以使用 curl 测试在 JVM 或 Native 模式下启动的应用程序。

获取 alice 的访问令牌

export access_token=$(\
    curl --insecure -X POST https://: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

curl -i -X GET \
  https://:8080/frontend/user-name-with-propagated-token \
  -H "Authorization: Bearer "$access_token

使用相同的令牌调用 /frontend/admin-name-with-propagated-token。与前面的命令相比,此命令返回 403,因为 alice 仅具有 user 角色

curl -i -X GET \
  https://:8080/frontend/admin-name-with-propagated-token \
  -H "Authorization: Bearer "$access_token

接下来,获取 admin 的访问令牌

export access_token=$(\
    curl --insecure -X POST https://: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

curl -i -X GET \
  https://:8080/frontend/user-name-with-propagated-token \
  -H "Authorization: Bearer "$access_token

使用相同的令牌调用 /frontend/admin-name-with-propagated-token。此命令还会返回 200 状态代码和名称 admin,因为 admin 具有 useradmin 角色

curl -i -X GET \
  https://:8080/frontend/admin-name-with-propagated-token \
  -H "Authorization: Bearer "$access_token

接下来,检查 FrontendResource 方法,该方法不传播现有令牌,而是使用 OidcClient 来获取和传播令牌。如前所示,OidcClient 配置为获取 alice 用户的令牌。

curl -i -X GET \
  https://:8080/frontend/user-name-with-oidc-client-token

此命令返回 200 状态代码和名称 alice

curl -i -X GET \
  https://:8080/frontend/admin-name-with-oidc-client-token

与前面的命令相比,此命令返回 403 状态代码。

接下来,测试以编程方式创建的 OIDC 客户端是否在反应式和命令式(阻塞)模式下都使用 RestClientWithTokenHeaderParam 正确获取和传播令牌。

调用 /user-name-with-oidc-client-token-header-param。此命令返回 200 状态代码和名称 alice

curl -i -X GET \
  https://:8080/frontend/user-name-with-oidc-client-token-header-param

调用 /admin-name-with-oidc-client-token-header-param。与前面的命令相比,此命令返回 403 状态代码

curl -i -X GET \
  https://: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 \
  https://: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 \
  https://:8080/frontend/admin-name-with-oidc-client-token-header-param-blocking

相关内容