Getting started with Security by using Basic authentication and Jakarta Persistence
通过内置 Quarkus Basic authentication 和 Jakarta Persistence 标识提供程序来保护 Quarkus 应用程序端点,从而开始使用 Quarkus 安全功能并启用基于角色的访问控制。
Jakarta Persistence IdentityProvider
验证和转换 Basic authentication 用户名和密码对来生成 SecurityIdentity
实例,该实例用于授权访问请求,从而确保 Quarkus 应用程序安全。
有关 Jakarta Persistence 的详细信息,请参阅 Quarkus Security with Jakarta Persistence 指南。
本教程指导您在 Quarkus 中实现更高级别的安全机制,例如如何使用 OpenID Connect (OIDC) 身份验证机制。
Prerequisites
如要完成本指南,您需要:
-
Roughly 15 minutes
-
An IDE
-
安装了 JDK 17+,已正确配置
JAVA_HOME
-
Apache Maven ${proposed-maven-version}
-
如果你想使用 Quarkus CLI, 则可以选择使用
-
如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately
Building your application
本教程提供了详细的步骤来创建具有端点的应用程序,这些端点演示了各种授权策略:
Endpoint | Description |
---|---|
|
无需身份验证即可访问,该端点允许匿名访问。 |
|
通过基于角色的访问控制 (RBAC) 保护,只有具有 |
|
同样受 RBAC 保护,只有具有 |
要检查完成的示例,请下载 $${quickstarts-base-url}/archive/main.zip[存档] 或克隆 Git 存储库:
您可以在 |
Create and verify the Maven project
为了使 Quarkus Security 能够将安全来源映射到 Jakarta Persistence 实体,确保本教程中的 Maven 项目包含 `quarkus-security-jpa`或 `quarkus-security-jpa-reactive`扩展。
Hibernate ORM with Panache用于存储您的用户身份,但您还可以使用 `quarkus-security-jpa`扩展与 Hibernate ORM一起使用。 Hibernate Reactive和 Hibernate Reactive with Panache都可以与 `quarkus-security-jpa-reactive`扩展一起使用。 您还必须添加您倾向的数据库连接器库。本示例教程中的说明为身份存储使用 PostgreSQL 数据库。 |
Create the Maven project
您可以使用安全 Jakarta Persistence 扩展创建新的 Maven 项目,也可以将扩展添加至现有的 Maven 项目。您可以使用 Hibernate ORM 或 Hibernate Reactive。
-
要使用 Jakarta Persistence 扩展创建新的 Maven 项目,请完成以下步骤之一:
-
要使用 Hibernate ORM 创建 Maven 项目,请使用以下命令:
-
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 指南。
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}"
-
要将 Jakarta Persistence 扩展添加到现有的 Maven 项目,请完成以下步骤之一:
-
要向使用 Hibernate ORM 的现有 Maven 项目添加安全 Jakarta Persistence 扩展,请从您的项目基本目录运行以下命令:====== :iokays-category: quarkus :iokays-path: modules/ROOT/pages/_includes/devtools/extension-add.adoc :keywords: Quarkus, 中文文档, 编程技术
-
-
quarkus extension add {add-extension-extensions}
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
./gradlew addExtension --extensions='{add-extension-extensions}'
-
要向使用 Hibernate Reactive 的现有 Maven 项目添加安全 Jakarta Persistence 扩展,请从您的项目基本目录运行以下命令:====== :iokays-category: quarkus :iokays-path: modules/ROOT/pages/_includes/devtools/extension-add.adoc :keywords: Quarkus, 中文文档, 编程技术
quarkus extension add {add-extension-extensions}
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
./gradlew addExtension --extensions='{add-extension-extensions}'
Verify the quarkus-security-jpa dependency
运行上述任一命令以创建 Maven 项目后,验证是否已将 `quarkus-security-jpa`依存关系添加到您的项目构建 XML 文件中。
-
要验证 `quarkus-security-jpa`扩展,请检查以下配置:======
.pom.xml
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-security-jpa</artifactId> </dependency>
implementation("io.quarkus:quarkus-security-jpa")
-
要验证 `quarkus-security-jpa-reactive`扩展,请检查以下配置:======
.pom.xml
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-security-jpa-reactive</artifactId> </dependency>
implementation("io.quarkus:quarkus-security-jpa-reactive")
Write the application
-
保护 API 端点以使用以下方法之一确定谁可以访问应用程序:
-
实施 `/api/public`端点以允许所有用户访问应用程序。向您的 Java 源代码中添加常规 Jakarta REST 资源,如下图所示:======
-
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"; } }
-
实施一个只有具有管理员角色的用户才能访问的 /api/admin 端点。`/api/admin`端点的源代码相似,但相反,您使用一个 `@RolesAllowed`注释来确保只有已授予 `admin`角色的用户才能访问该端点。使用以下 `@RolesAllowed`注释添加 Jakarta REST 资源:======
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`用户并返回他们的用户名,所有这些信息都是从数据库中检索的。======
package org.acme.security.jpa; import jakarta.annotation.security.RolesAllowed; import jakarta.inject.Inject; 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(); } }
== Define the user entity
-
您现在可以通过向 `user`实体添加注释来描述您希望如何在模型中存储安全信息,如下面的代码段所示:
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();
}
}
quarkus-security-jpa
扩展仅在单个实体使用 @UserDefinition
进行注释时才初始化。
1 | `@UserDefinition`注释必须存在于单个实体中,该实体可以是普通的 Hibernate ORM 实体,也可以是带有 Panache 实体的 Hibernate ORM。 |
2 | 指示用于用户名的字段。 |
3 | 指示用于密码的域。默认情况下,它使用 bcrypt 散列的密码。您可以对其进行配置以使用明文或自定义密码。 |
4 | 指示添加到目标主体表示属性的逗号分隔的角色列表。 |
5 | 允许我们在使用适当的 bcrypt 散列对密码进行散列时添加用户。 |
不要忘记设置 Panache 和 PostgreSQL JDBC 驱动程序,请参阅 Setting up and configuring Hibernate ORM with Panache 了解更多信息。
Hibernate Reactive Panache 使用 io.quarkus.hibernate.reactive.panache.PanacheEntity
而不是 io.quarkus.hibernate.orm.panache.PanacheEntity
。有关详细信息,请参阅 User file。
== Configure the application
-
通过将
quarkus.http.auth.basic
属性设置为true
来启用内置 Quarkus Basic authentication 机制:quarkus.http.auth.basic=true
当需要安全访问并且未启用其他身份验证机制时,Quarkus 的内置 Basic authentication 是备用身份验证机制。因此,在本教程中,无需将属性 quarkus.http.auth.basic
设置为 true
。
-
在
application.properties
文件中至少配置一个数据源,以便quarkus-security-jpa
扩展程序访问您的数据库。例如:======
quarkus.http.auth.basic=true quarkus.datasource.db-kind=postgresql quarkus.datasource.username=quarkus quarkus.datasource.password=quarkus quarkus.datasource.jdbc.url=jdbc:postgresql:security_jpa quarkus.hibernate-orm.database.generation=drop-and-create
-
要使用用户和角色初始化数据库,请实现
Startup
类,如下面的代码片段所示:
%prod.quarkus.datasource.reactive.url=vertx-reactive:postgresql://localhost:5431/security_jpa
|
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 哈希密码。
Test your application by using Dev Services for PostgreSQL
在生产模式下运行应用程序之前,使用 Dev Services for PostgreSQL 在 JVM 和原生模式下完成应用程序的集成测试。
首先将以下依赖项添加到测试项目:
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.rest-assured:rest-assured")
-
要在开发模式下运行应用程序:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
-
以下属性配置演示如何启用 PostgreSQL 测试仅在生产 (
prod
) 模式下运行。在此场景中,Dev Services for PostgreSQL
会启动并配置一个PostgreSQL
测试容器。
%prod.quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.username=quarkus
%prod.quarkus.datasource.password=quarkus
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql:elytron_security_jpa
quarkus.hibernate-orm.database.generation=drop-and-create
-
如果您添加
%prod.
配置文件前缀,数据源属性将对Dev Services for PostgreSQL
不可见,并且仅对在生产模式下运行的应用程序可见。 -
要编写集成测试,请使用以下代码示例:
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"));
}
}
如您在此代码示例中所见,您无需从测试代码启动测试容器。
当您在开发模式下启动应用程序时,PostgreSQL 的开发服务会启动一个 PostgreSQL 开发模式容器,以便您可以开始开发应用程序。在开发应用程序时,您可以使用 Continuous Testing 功能单独添加并运行测试。PostgreSQL 的开发服务通过提供一个单独的 PostgreSQL 测试容器(该容器与开发模式容器不冲突)来支持您在开发期间进行测试。 |
Use curl or a browser to test your application
-
使用以下示例启动 PostgreSQL 服务器:
docker run --rm=true --name security-getting-started -e POSTGRES_USER=quarkus \ -e POSTGRES_PASSWORD=quarkus -e POSTGRES_DB=elytron_security_jpa \ -p 5432:5432 postgres:14.1
Compile and run the application
-
使用以下方法之一编译并运行您的 Quarkus 应用程序:
-
JVM mode[style="arabic"]
-
Compile the application:====== :iokays-category: quarkus :iokays-path: modules/ROOT/pages/_includes/devtools/build.adoc :keywords: Quarkus, 中文文档, 编程技术
-
-
quarkus build
./mvnw install
./gradlew build
-
Run the application:======
java -jar target/quarkus-app/quarkus-run.jar
-
Native mode[style="arabic"]
-
Compile the application:====== :iokays-category: quarkus :iokays-path: modules/ROOT/pages/_includes/devtools/build-native.adoc :keywords: Quarkus, 中文文档, 编程技术
-
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
-
Run the application:======
./target/security-jpa-quickstart-1.0.0-SNAPSHOT-runner
Access and test the application security
当您的应用程序正在运行时,您可以使用以下 Curl 命令之一访问其端点。
-
以匿名方式连接到受保护端点:======
$ curl -i -X GET http://localhost:8080/api/public HTTP/1.1 200 OK Content-Length: 6 Content-Type: text/plain;charset=UTF-8 public
-
以匿名方式连接到受保护端点:======
$ curl -i -X GET http://localhost:8080/api/admin HTTP/1.1 401 Unauthorized Content-Length: 14 Content-Type: text/html;charset=UTF-8 WWW-Authenticate: Basic Not authorized
-
以授权用户的身份连接到受保护端点:======
$ curl -i -X GET -u admin:admin http://localhost:8080/api/admin HTTP/1.1 200 OK Content-Length: 5 Content-Type: text/plain;charset=UTF-8 admin
您还可以使用浏览器访问相同的端点 URL。
如果您使用浏览器以匿名方式连接到受保护资源,将显示一个基本身份验证表单,提示您输入凭据。
=== Results
当您提供授权用户的凭据(例如 admin:admin
)时,Jakarta Persistence 安全扩展将对用户进行身份验证并加载用户的角色。admin
用户被授权访问受保护资源。
如果资源受 @RolesAllowed("user")
保护,则用户 admin
无权访问该资源,因为它未分配给“用户”角色,如下例所示:
$ curl -i -X GET -u admin:admin http://localhost:8080/api/users/me
HTTP/1.1 403 Forbidden
Content-Length: 34
Content-Type: text/html;charset=UTF-8
Forbidden
最后,名为 user
的用户被授权,并且安全上下文包含主体详细信息,例如用户名。
$ curl -i -X GET -u user:user http://localhost:8080/api/users/me
HTTP/1.1 200 OK
Content-Length: 4
Content-Type: text/plain;charset=UTF-8
user
== What’s next
您已成功学习如何创建和测试安全的 Quarkus 应用程序。这是通过将 Quarkus 中内置的 Basic authentication 与 Jakarta Persistence 标识提供程序集成而实现的。
在完成本教程后,您可以在 Quarkus 中探索更高级的安全机制。以下信息显示了如何使用 OpenID Connect
通过安全单点登录访问 Quarkus 端点:
== References