编辑此页面

使用基本身份验证和 Jakarta Persistence 开始使用安全性

通过使用内置的 Quarkus 基本身份验证和 Jakarta Persistence 身份提供程序保护您的 Quarkus 应用程序端点,从而开始使用 Quarkus Security,从而启用基于角色的访问控制。

Jakarta Persistence IdentityProvider 验证 基本身份验证 用户名和密码对,并将其转换为 SecurityIdentity 实例,该实例用于授权访问请求,从而保护您的 Quarkus 应用程序的安全。

有关 Jakarta Persistence 的更多信息,请参阅Quarkus Security with Jakarta Persistence 指南。

本教程将帮助您在 Quarkus 中实现更高级的安全机制,例如,如何使用 OpenID Connect (OIDC) 身份验证机制。

先决条件

要完成本指南,您需要

  • 大约 15 分钟

  • 一个 IDE

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

  • Apache Maven 3.9.9

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

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

构建您的应用程序

本教程详细介绍了如何创建一个具有端点的应用程序,这些端点说明了各种授权策略

端点 描述

/api/public

无需身份验证即可访问,此端点允许匿名访问。

/api/admin

通过基于角色的访问控制 (RBAC) 保护,此端点仅供具有 admin 角色的用户访问。通过使用 @RolesAllowed 注释以声明方式控制访问。

/api/users/me

也受到 RBAC 的保护,此端点仅供具有 user 角色的用户访问。它将调用者的用户名作为字符串返回。

要检查已完成的示例,请下载存档或克隆 Git 存储库

git clone https://github.com/quarkusio/quarkus-quickstarts.git

您可以在 security-jpa-quickstart 目录中找到解决方案。

1. 创建并验证 Maven 项目

为了让 Quarkus Security 能够将您的安全源映射到 Jakarta Persistence 实体,请确保本教程中的 Maven 项目包含 quarkus-security-jpaquarkus-security-jpa-reactive 扩展。

Hibernate ORM with Panache 用于存储您的用户身份,但您也可以使用带有 quarkus-security-jpa 扩展的Hibernate ORM

quarkus-security-jpa-reactive 扩展可以使用 Hibernate ReactiveHibernate Reactive with Panache

您还必须添加您首选的数据库连接器库。本示例教程中的说明使用 PostgreSQL 数据库作为身份存储。

1.1. 创建 Maven 项目

您可以创建一个新的带有 Security Jakarta Persistence 扩展的 Maven 项目,或者将该扩展添加到现有的 Maven 项目。您可以使用 Hibernate ORM 或 Hibernate Reactive。

1.1.1. 创建新的 Maven 项目

  • 要创建一个新的带有 Jakarta Persistence 扩展的 Maven 项目,请完成以下步骤之一

    • 要创建带有 Hibernate ORM 的 Maven 项目,请使用以下命令

CLI
quarkus create app org.acme:security-jpa-quickstart \
    --extension='security-jpa,jdbc-postgresql,rest,hibernate-orm-panache' \
    --no-code
cd security-jpa-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-jpa-quickstart \
    -Dextensions='security-jpa,jdbc-postgresql,rest,hibernate-orm-panache' \
    -DnoCode
cd security-jpa-quickstart

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

对于 Windows 用户

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

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

1.1.2. 将 Jakarta Persistence 扩展添加到现有项目

  • 要将 Jakarta Persistence 扩展添加到现有的 Maven 项目,请完成以下步骤之一

    • 要将 Security Jakarta Persistence 扩展添加到带有 Hibernate ORM 的现有 Maven 项目,请从您的项目基本目录运行以下命令

      CLI
      quarkus extension add security-jpa
      Maven
      ./mvnw quarkus:add-extension -Dextensions='security-jpa'
      Gradle
      ./gradlew addExtension --extensions='security-jpa'
    • 要将 Security Jakarta Persistence 扩展添加到带有 Hibernate Reactive 的现有 Maven 项目,请从您的项目基本目录运行以下命令

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

1.2. 验证 quarkus-security-jpa 依赖项

在您运行上述任一命令以创建 Maven 项目后,请验证 quarkus-security-jpa 依赖项是否已添加到您的项目构建 XML 文件中。

  • 要验证 quarkus-security-jpa 扩展,请检查以下配置

    pom.xml
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-security-jpa</artifactId>
    </dependency>
    build.gradle
    implementation("io.quarkus:quarkus-security-jpa")
  • 要验证 quarkus-security-jpa-reactive 扩展,请检查以下配置

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

2. 编写应用程序

  • 通过使用以下方法之一来保护 API 端点,以确定谁可以访问应用程序

    • 实现 /api/public 端点以允许所有用户访问应用程序。将常规 Jakarta REST 资源添加到您的 Java 源代码,如以下代码片段所示

      src/main/java/org/acme/security/jpa/PublicResource.java
      package org.acme.security.jpa;
      
      import jakarta.annotation.security.PermitAll;
      import jakarta.ws.rs.GET;
      import jakarta.ws.rs.Path;
      import jakarta.ws.rs.Produces;
      import jakarta.ws.rs.core.MediaType;
      
      @Path("/api/public")
      public class PublicResource {
      
          @GET
          @PermitAll
          @Produces(MediaType.TEXT_PLAIN)
          public String publicResource() {
              return "public";
         }
      }
    • 实现一个只能由具有 admin 角色的用户访问的 /api/admin 端点。/api/admin 端点的源代码类似,但您可以使用 @RolesAllowed 注释来确保只有被授予 admin 角色的用户才能访问该端点。添加一个带有以下 @RolesAllowed 注释的 Jakarta REST 资源

      src/main/java/org/acme/security/jpa/AdminResource.java
      package org.acme.security.jpa;
      
      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 adminResource() {
               return "admin";
          }
      }
    • 实现一个只能由具有 user 角色的用户访问的 /api/users/me 端点。使用 SecurityContext 来访问当前经过身份验证的 Principal 用户并返回他们的用户名,所有这些都从数据库中检索。

      src/main/java/org/acme/security/jpa/UserResource.java
      package org.acme.security.jpa;
      
      import jakarta.annotation.security.RolesAllowed;
      import jakarta.ws.rs.GET;
      import jakarta.ws.rs.Path;
      import jakarta.ws.rs.core.Context;
      import jakarta.ws.rs.core.SecurityContext;
      
      @Path("/api/users")
      public class UserResource {
      
          @GET
          @RolesAllowed("user")
          @Path("/me")
          public String me(@Context SecurityContext securityContext) {
              return securityContext.getUserPrincipal().getName();
          }
      }

3. 定义用户实体

通过将以下注释添加到 user 实体来指定安全信息在模型中的存储方式

src/main/java/org/acme/security/jpa/User.java
package org.acme.security.jpa;

import jakarta.persistence.Entity;
import jakarta.persistence.Table;

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import io.quarkus.elytron.security.common.BcryptUtil;
import io.quarkus.security.jpa.Password;
import io.quarkus.security.jpa.Roles;
import io.quarkus.security.jpa.UserDefinition;
import io.quarkus.security.jpa.Username;

@Entity
@Table(name = "test_user")
@UserDefinition (1)
public class User extends PanacheEntity {
    @Username (2)
    public String username;
    @Password (3)
    public String password;
    @Roles (4)
    public String role;

    /**
     * Adds a new user to the database
     * @param username the username
     * @param password the unencrypted password (it is encrypted with bcrypt)
     * @param role the comma-separated roles
     */
    public static void add(String username, String password, String role) { (5)
        User user = new User();
        user.username = username;
        user.password = BcryptUtil.bcryptHash(password);
        user.role = role;
        user.persist();
    }
}

只有当单个实体使用 @UserDefinition 注释时,才会初始化 quarkus-security-jpa 扩展。

1 @UserDefinition 注释必须存在于单个实体上,无论是常规 Hibernate ORM 实体还是带有 Panache 的 Hibernate ORM 实体。
2 指示用于用户名字段。
3 指示用于密码的字段。默认情况下,它使用 bcrypt 哈希密码。您可以将其配置为使用纯文本或自定义密码。
4 指示添加到目标主体表示属性的逗号分隔的角色列表。
5 允许我们在使用适当的 bcrypt 哈希值哈希密码的同时添加用户。

不要忘记设置 Panache 和 PostgreSQL JDBC 驱动程序,请参阅设置和配置带有 Panache 的 Hibernate ORM 以获取更多信息。

Hibernate Reactive Panache 使用 io.quarkus.hibernate.reactive.panache.PanacheEntity 而不是 io.quarkus.hibernate.orm.panache.PanacheEntity。有关更多信息,请参阅用户文件

4. 配置应用程序

  1. 通过将 quarkus.http.auth.basic 属性设置为 true 来启用内置的 Quarkus 基本身份验证机制

    quarkus.http.auth.basic=true

    当需要安全访问且未启用其他身份验证机制时,Quarkus 的内置基本身份验证是后备身份验证机制。因此,在本教程中,您不需要将属性 quarkus.http.auth.basic 设置为 true

  2. src/main/resources/application.properties 文件中配置至少一个数据源,以便 quarkus-security-jpa 扩展可以访问您的数据库。例如

    src/main/resources/application.properties
    quarkus.http.auth.basic=true
    
    %prod.quarkus.datasource.db-kind=postgresql
    %prod.quarkus.datasource.username=quarkus
    %prod.quarkus.datasource.password=quarkus
    %prod.quarkus.datasource.jdbc.url=jdbc:postgresql:quarkus
    
    quarkus.hibernate-orm.schema-management.strategy=drop-and-create

    通过添加 %prod. 配置文件前缀,您可以确保数据源属性仅由在生产模式下运行的应用程序观察到。

  3. 要使用用户和角色初始化数据库,请实现 Startup 类,如以下代码片段中所述

  • quarkus-security-jpa-reactive 扩展使用的 Reactive 数据源的 URL 是使用 quarkus.datasource.reactive.url 配置属性设置的,而不是通常由 JDBC 数据源使用的 quarkus.datasource.jdbc.url 配置属性。

    src/main/resources/application.properties
    %prod.quarkus.datasource.reactive.url=vertx-reactive:postgresql://:5431/security_jpa
  • 在本教程中,PostgreSQL 数据库用于身份存储。Hibernate ORM 会在启动时自动创建数据库架构。这种方法适用于开发,但不建议用于生产。因此,在生产环境中需要进行调整。

src/main/java/org/acme/security/jpa/Startup.java
package org.acme.security.jpa;

import jakarta.enterprise.event.Observes;
import jakarta.inject.Singleton;
import jakarta.transaction.Transactional;

import io.quarkus.runtime.StartupEvent;


@Singleton
public class Startup {
    @Transactional
    public void loadUsers(@Observes StartupEvent evt) {
        // reset and load all test users
        User.deleteAll();
        User.add("admin", "admin", "admin");
        User.add("user", "user", "user");
    }
}

前面的示例演示了如何保护应用程序以及如何提供由指定数据库提供的身份。

在生产环境中,不要存储纯文本密码。因此,quarkus-security-jpa 默认为使用 bcrypt 哈希密码。

5. 使用适用于 PostgreSQL 的 Dev Services 在开发模式下测试您的应用程序

在使用生产模式运行应用程序之前,请使用适用于 PostgreSQL 的 Dev Services完成 JVM 和本机模式下的应用程序集成测试。

首先将以下依赖项添加到您的测试项目中

pom.xml
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <scope>test</scope>
</dependency>
build.gradle
testImplementation("io.rest-assured:rest-assured")

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

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

在这种情况下,适用于 PostgreSQL 的 Dev Services 启动并配置一个 PostgreSQL 测试容器。确保您的计算机上安装了 PodmanDocker

要编写集成测试,请使用以下代码示例

src/test/java/org/acme/security/jpa/JpaSecurityRealmTest.java
package org.acme.security.jpa;

import static io.restassured.RestAssured.get;
import static io.restassured.RestAssured.given;
import static org.hamcrest.core.Is.is;

import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class JpaSecurityRealmTest {

    @Test
    void shouldAccessPublicWhenAnonymous() {
        get("/api/public")
                .then()
                .statusCode(HttpStatus.SC_OK);

    }

    @Test
    void shouldNotAccessAdminWhenAnonymous() {
        get("/api/admin")
                .then()
                .statusCode(HttpStatus.SC_UNAUTHORIZED);

    }

    @Test
    void shouldAccessAdminWhenAdminAuthenticated() {
        given()
                .auth().preemptive().basic("admin", "admin")
                .when()
                .get("/api/admin")
                .then()
                .statusCode(HttpStatus.SC_OK);

    }

    @Test
    void shouldNotAccessUserWhenAdminAuthenticated() {
        given()
                .auth().preemptive().basic("admin", "admin")
                .when()
                .get("/api/users/me")
                .then()
                .statusCode(HttpStatus.SC_FORBIDDEN);
    }

    @Test
    void shouldAccessUserAndGetIdentityWhenUserAuthenticated() {
        given()
                .auth().preemptive().basic("user", "user")
                .when()
                .get("/api/users/me")
                .then()
                .statusCode(HttpStatus.SC_OK)
                .body(is("user"));
    }
}

如您在此代码示例中所见,您无需从测试代码启动测试容器。

要运行这些测试,请选择 Press [r] to resume testing 选项,该选项在您在开发模式下启动应用程序后显示在控制台中。

当您在开发模式下启动应用程序时,适用于 PostgreSQL 的 Dev Services 会启动一个 PostgreSQL 开发模式容器,以便您可以开始开发您的应用程序。在开发应用程序时,您可以使用Continuous Testing功能单独添加和运行测试。适用于 PostgreSQL 的 Dev Services 支持在您开发时进行测试,方法是提供一个单独的 PostgreSQL 测试容器,该容器与开发模式容器不冲突。

或者,您可以使用 Maven 运行这些测试

./mvnw test

6. 使用 Curl 或浏览器在生产模式下测试您的应用程序

要使用 Curl 或浏览器测试您的应用程序,请首先启动 PostgreSQL 服务器。然后,在 JVM 或本机模式下编译并运行您的应用程序。

6.1. 启动 PostgreSQL 服务器

docker run --rm=true --name security-getting-started -e POSTGRES_USER=quarkus \
           -e POSTGRES_PASSWORD=quarkus -e POSTGRES_DB=quarkus \
           -p 5432:5432 postgres:17

6.2. 编译并运行应用程序

  • 通过使用以下方法之一来编译并运行您的 Quarkus 应用程序

    • JVM 模式

      1. 编译应用程序

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

        java -jar target/quarkus-app/quarkus-run.jar
    • 本机模式

      1. 编译应用程序

        CLI
        quarkus build --native
        Maven
        ./mvnw install -Dnative
        Gradle
        ./gradlew build -Dquarkus.native.enabled=true
      2. 运行应用程序

        ./target/security-jpa-quickstart-1.0.0-SNAPSHOT-runner

6.3. 使用 Curl 访问并测试应用程序安全性

当您的应用程序正在运行时,您可以使用以下 Curl 命令之一来访问其端点。

  • 以匿名方式连接到受保护的端点

    $ curl -i -X GET https://:8080/api/public
    
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: text/plain;charset=UTF-8
    
    public
  • 以匿名方式连接到受保护的端点

    $ curl -i -X GET https://:8080/api/admin
    
    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: Basic
  • 以授权用户身份连接到受保护的端点

    $ curl -i -X GET -u admin:admin https://:8080/api/admin
    
    HTTP/1.1 200 OK
    Content-Length: 5
    Content-Type: text/plain;charset=UTF-8
    
    admin

您也可以使用浏览器访问相同的端点 URL。

6.4. 使用浏览器访问并测试应用程序安全性

如果您使用浏览器以匿名方式连接到受保护的资源,则会显示一个基本身份验证表单,提示您输入凭据。

6.5. 结果

当您提供授权用户的凭据(例如,admin:admin)时,Jakarta Persistence 安全扩展会进行身份验证并加载用户的角色。admin 用户有权访问受保护的资源。

如果资源受到 @RolesAllowed("user") 的保护,则用户 admin 无权访问该资源,因为它未分配给“user”角色,如以下示例所示

$ curl -i -X GET -u admin:admin https://:8080/api/users/me

HTTP/1.1 403 Forbidden

最后,名为 user 的用户已获得授权,并且安全上下文包含主要详细信息,例如用户名。

$ curl -i -X GET -u user:user https://:8080/api/users/me

HTTP/1.1 200 OK
Content-Length: 4
Content-Type: text/plain;charset=UTF-8

user

下一步是什么

您已成功学习了如何创建和测试安全的 Quarkus 应用程序。这是通过将 Quarkus 中内置的基本身份验证与 Jakarta Persistence 身份提供程序集成来实现的。

完成本教程后,您可以在 Quarkus 中探索更高级的安全机制。以下信息向您展示了如何使用 OpenID Connect 安全地单点登录到您的 Quarkus 端点

相关内容