Protect a service application by using OpenID Connect (OIDC) Bearer token authentication

使用 Quarkus OpenID Connect (OIDC) 扩展通过 Bearer 令牌验证保护 Jakarta REST 应用程序。载体令牌由 OIDC 和 OAuth 2.0 兼容授权服务器(如 Keycloak)颁发。 有关 OIDC Bearer 令牌验证的详情,请参阅 Quarkus OpenID Connect (OIDC) Bearer token authentication指南。 如果您想通过使用 OIDC 授权代码流程验证来保护 Web 应用程序,请参阅 OpenID Connect authorization code flow mechanism for protecting web applications指南。

Prerequisites

如要完成本指南,您需要:

  • Roughly 15 minutes

  • An IDE

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

  • Apache Maven ${proposed-maven-version}

  • 如果你想使用 Quarkus CLI, 则可以选择使用

  • 如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately

Architecture

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

  • /api/users/me

  • /api/admin

这些端点受保护并且仅在客户端发送携带请求的 bearer 令牌时才能访问,该令牌必须有效(例如,签名、到期时间和受众)并且受微服务信任。

Keycloak 服务器颁发携带令牌,并代表颁发令牌的主体。因为它是一个 OAuth 2.0 授权服务器,所以令牌还引用代表用户执行操作的客户端。

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

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

Solution

按照下一部分中的说明逐步创建应用程序。你也可以直接转到完整示例。

你可以通过运行命令 git clone $${quickstarts-base-url}.git 克隆 Git 存储库,或者你可以下载一个 $${quickstarts-base-url}/archive/main.zip[archive]。

解决方案位于 security-openid-connect-quickstart directory

Create the Maven project

你可以使用 oidc 扩展名创建一个新的 Maven 项目,或者可以将此扩展名添加到现有的 Maven 项目。完成以下命令之一:

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

CLI
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
    --no-code
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 --gradle--gradle-kotlin-dsl 选项。 有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。

Maven
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
    -DprojectGroupId={create-app-group-id} \
    -DprojectArtifactId={create-app-artifact-id} \
    -DnoCode
cd {create-app-artifact-id}

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

适用于 Windows 用户:

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

  • 如果使用 Powershell,将 -D 参数用双引号引起来,例如 "-DprojectArtifactId={create-app-artifact-id}"

如果你的 Quarkus 项目已经配置好,你可以通过在项目基础目录中运行以下命令将 oidc 扩展名添加到项目:

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

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

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

Write the application

  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;
        }
    }
}
  1. 按照以下示例中所示实现 /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 角色的用户才能访问此端点。

@RequestScoped@ApplicationScoped 上下文中都支持注入 SecurityIdentity

Configure the application

  • 通过在 src/main/resources/application.properties 文件中设置以下配置属性来配置 Quarkus OpenID Connect (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 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 会启动一个容器。有关更多信息,请参阅 Run the application in dev mode 部分。

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

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

有关更多信息,请参阅 Quarkus OpenID Connect (OIDC) configuration properties 指南。

== Start and configure the Keycloak server

  1. realm configuration file 放在类路径(target/classes 目录)上,以便在 dev 模式下运行时自动导入它。如果你已经构建了 complete solution,则不必执行此操作,在这种情况下,此 realm 文件会在构建过程中添加到类路径。

在开发模式中运行应用程序时不要启动 Keycloak 服务器;Dev Services for Keycloak 将启动容器。要了解详细信息,请参阅 Run the application in dev mode 部分。

  1. 若要启动 Keycloak 服务器,可以使用 Docker 运行以下命令:======

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 或更高版本。

    1. 可以在 localhost:8180 处访问 Keycloak 服务器。

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

  • Username: admin

  • Password: admin

    1. 从上游社区存储库导入 realm configuration file 以创建新领域。

要了解详细信息,请参阅关于 creating and configuring a new realm 的 Keycloak 文档。

如果要使用 Keycloak 管理客户端通过应用程序配置服务器,则需要包含 quarkus-keycloak-admin-rest-client 或(如果应用程序使用 quarkus-rest-clientquarkus-keycloak-admin-resteasy-client 扩展。要了解详细信息,请参阅 Quarkus Keycloak Admin Client 指南。

Run the application in dev mode

  1. 若要在 dev 模式中运行应用程序,请运行以下命令:====== :iokays-category: quarkus :iokays-path: modules/ROOT/pages/_includes/devtools/dev.adoc :keywords: Quarkus, 中文文档, 编程技术

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev
  • Dev Services for Keycloak 将启动 Keycloak 容器并导入 quarkus-realm.json

    1. 打开 Dev UI,可在 /q/dev-ui 处找到。然后,在 OpenID Connect 卡片中,单击 Keycloak provider 链接。

    2. 当系统提示登录 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 状态码。

== Run the Application in JVM mode

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

  1. Compile the application:====== :iokays-category: quarkus :iokays-path: modules/ROOT/pages/_includes/devtools/build.adoc :keywords: Quarkus, 中文文档, 编程技术

CLI
quarkus build
Maven
./mvnw install
Gradle
./gradlew build
  1. Run the application:======

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

== Run the application in native mode

可以将同一 demo 原样编译为本地模式,而无需任何修改。这意味着不再需要在生产环境上安装 JVM。运行时技术包含在生成的二进制文件中,并经过优化,可以以所需的最小资源来运行。

编译花费的时间稍微长一点,所以默认情况下会禁用此步骤。

  1. 通过启用 native 配置文件重新构建你的应用程序:====== :iokays-category: quarkus :iokays-path: modules/ROOT/pages/_includes/devtools/build-native.adoc :keywords: Quarkus, 中文文档, 编程技术

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

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

== Test the application

有关在开发模式下测试应用程序的信息,请参阅前面的 Run the application in dev mode 部分。

您可以使用`curl`测试在 JVM 或本地模式中启动的应用程序。

  • 由于应用程序使用持有者令牌身份验证,因此你必须首先从 Keycloak 服务器获取访问令牌才能访问应用程序资源:

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

前面的示例为用户 alice 获取了一个访问令牌。

curl -v -X GET \
  http://localhost:8080/api/users/me \
  -H "Authorization: Bearer "$access_token
  • 只有具有 admin 角色的用户才能访问 http://localhost:8080/api/admin 终端节点。如果你尝试使用之前颁发的访问令牌访问此终端节点,你将从服务器那里获得一个 403 响应。

curl -v -X GET \
   http://localhost:8080/api/admin \
   -H "Authorization: Bearer "$access_token
  • 要访问管理员终端节点,请为 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' \
 )

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

== References