编辑此页面

使用 OpenID Connect (OIDC) Bearer 令牌身份验证保护服务应用程序

使用 Quarkus OpenID Connect (OIDC) 扩展程序,通过 Bearer 令牌认证来保护 Jakarta REST 应用程序。Bearer 令牌由符合 OIDC 和 OAuth 2.0 的授权服务器颁发,例如 Keycloak

有关 OIDC Bearer 令牌认证的更多信息,请参阅 Quarkus OpenID Connect (OIDC) Bearer 令牌认证指南。

如果要通过使用 OIDC 授权代码流认证来保护 Web 应用程序,请参阅用于保护 Web 应用程序的 OpenID Connect 授权代码流机制指南。

先决条件

要完成本指南,您需要

  • 大约 15 分钟

  • 一个 IDE

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

  • Apache Maven 3.9.9

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

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

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

  • jq 命令行处理器工具

架构

此示例演示了如何构建一个提供两个端点的简单微服务

  • /api/users/me

  • /api/admin

这些端点受到保护,只有当客户端发送一个 Bearer 令牌以及请求时才能访问,该令牌必须有效(例如,签名、过期时间和受众)并且受到微服务的信任。

Keycloak 服务器颁发 Bearer 令牌,并表示令牌颁发的主题。因为它是一个 OAuth 2.0 授权服务器,所以该令牌还引用了代表用户行事的客户端。

任何具有有效令牌的用户都可以访问 /api/users/me 端点。作为响应,它会返回一个 JSON 文档,其中包含从令牌信息中获取的用户详细信息。

/api/admin 端点受 RBAC(基于角色的访问控制)保护,只有具有 admin 角色的用户才能访问。在此端点,@RolesAllowed 注释用于声明性地强制执行访问约束。

解决方案

按照以下部分的说明,逐步创建应用程序。您也可以直接转到已完成的示例。

您可以通过运行命令 git clone https://github.com/quarkusio/quarkus-quickstarts.git 来克隆 Git 存储库,也可以下载一个 存档

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

创建 Maven 项目

您可以创建一个带有 oidc 扩展程序的新 Maven 项目,也可以将扩展程序添加到现有的 Maven 项目。完成以下命令之一

要创建一个新的 Maven 项目,请使用以下命令

CLI
quarkus create app org.acme:security-openid-connect-quickstart \
    --extension='oidc,rest-jackson' \
    --no-code
cd security-openid-connect-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-quickstart \
    -Dextensions='oidc,rest-jackson' \
    -DnoCode
cd security-openid-connect-quickstart

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

对于 Windows 用户

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

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

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

CLI
quarkus extension add oidc
Maven
./mvnw quarkus:add-extension -Dextensions='oidc'
Gradle
./gradlew addExtension --extensions='oidc'

这会将以下内容添加到您的构建文件中

pom.xml
<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-oidc</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-oidc")

编写应用程序

  1. 按以下示例所示实现 /api/users/me 端点,这是一个常规的 Jakarta REST 资源

    package org.acme.security.openid.connect;
    
    import jakarta.annotation.security.RolesAllowed;
    import jakarta.inject.Inject;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;
    
    import org.jboss.resteasy.reactive.NoCache;
    import io.quarkus.security.identity.SecurityIdentity;
    
    @Path("/api/users")
    public class UsersResource {
    
        @Inject
        SecurityIdentity securityIdentity;
    
        @GET
        @Path("/me")
        @RolesAllowed("user")
        @NoCache
        public User me() {
            return new User(securityIdentity);
        }
    
        public static class User {
    
            private final String userName;
    
            User(SecurityIdentity securityIdentity) {
                this.userName = securityIdentity.getPrincipal().getName();
            }
    
            public String getUserName() {
                return userName;
            }
        }
    }
  2. 按以下示例所示实现 /api/admin 端点

    package org.acme.security.openid.connect;
    
    import jakarta.annotation.security.RolesAllowed;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.Produces;
    import jakarta.ws.rs.core.MediaType;
    
    @Path("/api/admin")
    public class AdminResource {
    
        @GET
        @RolesAllowed("admin")
        @Produces(MediaType.TEXT_PLAIN)
        public String admin() {
            return "granted";
        }
    }

    此示例的主要区别在于,@RolesAllowed 注释用于验证只有被授予 admin 角色的用户才能访问该端点。

SecurityIdentity 的注入在 @RequestScoped@ApplicationScoped 上下文中均受支持。

配置应用程序

  • 通过在 src/main/resources/application.properties 文件中设置以下配置属性来配置 Quarkus OpenID Connect (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 not effective when running the application in JVM or native modes
    
    quarkus.keycloak.devservices.realm-path=quarkus-realm.json

其中

  • %prod.quarkus.oidc.auth-server-url 设置 OpenID Connect (OIDC) 服务器的基本 URL。%prod. 配置文件前缀确保您在开发(dev)模式下运行应用程序时,Dev Services for Keycloak 会启动一个容器。有关更多信息,请参阅在开发模式下运行应用程序部分。

  • quarkus.oidc.client-id 设置一个客户端 ID,用于标识应用程序。

  • quarkus.oidc.credentials.secret 设置客户端密钥,该密钥由 client_secret_basic 身份验证方法使用。

有关更多信息,请参阅 Quarkus OpenID Connect (OIDC) 配置属性指南。

启动并配置 Keycloak 服务器

  1. realm 配置文件放在类路径(target/classes 目录)上,以便在开发模式下运行时自动导入。如果您已经构建了一个 完整的解决方案,则无需执行此操作,在这种情况下,此 realm 文件将在构建期间添加到类路径。

    在开发模式下运行应用程序时,请勿启动 Keycloak 服务器;Dev Services for Keycloak 将启动一个容器。有关更多信息,请参阅在开发模式下运行应用程序部分。

  2. 要启动 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 或更高版本。

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

  4. 要访问 Keycloak 管理控制台,请使用以下登录凭据以 admin 用户身份登录

    • 用户名:admin

    • 密码:admin

  5. 从上游社区存储库导入 realm 配置文件以创建一个新的 realm。

有关更多信息,请参阅 Keycloak 文档中关于创建和配置新 realm的内容。

要通过使用 Keycloak Admin Client 从您的应用程序配置 Keycloak 服务器,请根据您的设置包含以下扩展程序之一

  • 对于 Quarkus REST:如果您正在使用 quarkus-restquarkus-rest-client 或两者,请包含 quarkus-keycloak-admin-rest-client 扩展程序。

  • 对于 RESTEasy Classic:如果您正在使用 quarkus-resteasyquarkus-resteasy-client 或两者,请包含 quarkus-keycloak-admin-resteasy-client 扩展程序。

  • 如果未显式使用 REST 层:建议包含 quarkus-keycloak-admin-rest-client 扩展程序。

这些指南确保 Keycloak Admin Client 与您的 REST 框架无缝集成,无论您是使用 REST 服务器、REST 客户端还是两者。

有关更多信息,请参阅 Quarkus Keycloak Admin Client 指南。

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

  1. 要在开发模式下运行应用程序,请运行以下命令

    CLI
    quarkus dev
    Maven
    ./mvnw quarkus:dev
    Gradle
    ./gradlew --console=plain quarkusDev
  2. 打开一个 Dev UI,您可以在 /q/dev-ui 中找到它。然后,在 OpenID Connect 卡中,单击 Keycloak provider 链接。

  3. 当提示您登录到 OpenID Connect Dev UI 提供的 Single Page Application 时,请执行以下步骤

    • alice(密码:alice)身份登录,她具有 user 角色。

      • 访问 /api/admin 返回 403 状态代码。

      • 访问 /api/users/me 返回 200 状态代码。

    • 注销并以 admin(密码:admin)身份再次登录,他同时具有 adminuser 角色。

      • 访问 /api/admin 返回 200 状态代码。

      • 访问 /api/users/me 返回 200 状态代码。

在 JVM 模式下运行应用程序

完成开发模式后,您可以将应用程序作为标准 Java 应用程序运行。

  1. 编译应用程序

    CLI
    quarkus build
    Maven
    ./mvnw install
    Gradle
    ./gradlew build
  2. 运行应用程序

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

在原生模式下运行应用程序

您可以将此相同的演示按原样编译为原生模式,而无需进行任何修改。这意味着您不再需要在生产环境中安装 JVM。运行时技术包含在生成的二进制文件中,并经过优化,可以以最少的资源运行。

编译需要一段时间,因此默认情况下禁用此步骤。

  1. 通过启用 native 配置文件再次构建您的应用程序

    CLI
    quarkus build --native
    Maven
    ./mvnw install -Dnative
    Gradle
    ./gradlew build -Dquarkus.native.enabled=true
  2. 稍等片刻后,您可以直接运行以下二进制文件

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

测试应用程序

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

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

  • 由于应用程序使用 Bearer 令牌认证,因此您必须首先从 Keycloak 服务器获取访问令牌才能访问应用程序资源

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' \
 )

quarkus.oidc.authentication.user-info-required 属性设置为 true 以要求使用访问令牌请求 UserInfo 时,您必须向令牌授予请求命令添加 scope=openid 查询参数,例如

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&scope=openid' | jq --raw-output '.access_token' \
 )

前面的示例获取用户 alice 的访问令牌。

  • 任何用户都可以访问 https://:8080/api/users/me 端点,该端点返回一个 JSON 有效负载,其中包含有关用户的详细信息。

curl -v -X GET \
  https://:8080/api/users/me \
  -H "Authorization: Bearer "$access_token
  • 只有具有 admin 角色的用户才能访问 https://:8080/api/admin 端点。如果您尝试使用先前颁发的访问令牌访问此端点,您将收到服务器的 403 响应。

curl -v -X GET \
   https://:8080/api/admin \
   -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' \
 )

有关编写依赖于 Dev Services for Keycloak 的集成测试的信息,请参阅“OpenID Connect (OIDC) Bearer 令牌认证”指南的 Dev Services for Keycloak 部分。

相关内容