Secure a Quarkus application with Basic authentication and Jakarta Persistence
Secure your Quarkus application endpoints by combining the built-in Quarkus Basic authentication with the Jakarta Persistence identity provider to enable role-based access control (RBAC).
The Jakarta Persistence IdentityProvider creates a SecurityIdentity instance, which is used during user authentication to verify and authorize access requests making your Quarkus application secure.
Jakarta Persistence の詳細は、Quarkus Security と Jakarta Persistence ガイドを参照してください。
このチュートリアルでは、Quarkusでより高度なセキュリティメカニズムを実装するための準備、たとえば、OpenID Connect(OIDC)認証メカニズムの使用方法について説明します。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 11+ がインストールされ、
JAVA_HOMEが適切に設定されていること -
Apache Maven 3.9.3
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
何を構築するか
このチュートリアルでは、さまざまな認可ポリシーを示すために、次のエンドポイントを提供するアプリケーションを構築する手順を説明します:
| エンドポイント | 説明 |
|---|---|
|
|
|
The |
|
The |
|
完了した例を確認するには、https://github.com/quarkusio/quarkus-quickstarts/archive/3.2.zip[アーカイブ] をダウンロードするか、Git リポジトリーのクローンを作成します。
You can find the solution in the |
1. Maven プロジェクトの作成および検証
For Quarkus Security to be able to map your security source to Jakarta Persistence entities, ensure that the Maven project that is used in this tutorial includes the security-jpa or the security-jpa-reactive extension.
|
Hibernate ORM with Panache is used to store your user identities but you can also use Hibernate ORM with the 優先するデータベースコネクターライブラリーも追加する必要があります。 このサンプルチュートリアルの手順では、アイデンティティーストアに PostgreSQL データベースを使用します。 |
1.1. Maven プロジェクトの作成
You can either create a new Maven project with the Security Jakarta Persistence extension or you can add the extension to an existing Maven project. You can use either Hibernate ORM or Hibernate Reactive.
-
Jakarta Persistence エクステンションで新しい Maven プロジェクトを作成するには、以下のいずれかの手順を実行します。
-
Hibernate ORM を使用して Maven プロジェクトを作成するには、次のコマンドを使用します。
-
To create the Maven project with Hibernate Reactive, use the following command:
-
-
既存の Maven プロジェクトに Jakarta Persistence エクステンションを追加するには、次のいずれかの手順を実行します:
-
Security Jakarta Persistence エクステンションを、Hibernate ORM を使用する既存の Maven プロジェクトに追加するには、プロジェクトのベース・ディレクトリーから以下のコマンドを実行します:
CLIquarkus extension add 'security-jpa'Maven./mvnw quarkus:add-extension -Dextensions='security-jpa'Gradle./gradlew addExtension --extensions='security-jpa' -
Hibernate Reactive を使用する既存の Maven プロジェクトに Security Jakarta Persistence エクステンションを追加するには、プロジェクトのベース・ディレクトリーから以下のコマンドを実行します:
CLIquarkus 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 依存関係の確認
After you have run either of the preceding commands to create the Maven project, verify that the security-jpa dependency was added to your project build XML file.
-
To verify the
security-jpaextension, check for the following configuration:pom.xml<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-security-jpa</artifactId> </dependency>build.gradleimplementation("io.quarkus:quarkus-security-jpa") -
To verify the
security-jpa-reactiveextension, check for the following configuration:pom.xml<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-security-jpa-reactive</artifactId> </dependency>build.gradleimplementation("io.quarkus:quarkus-security-jpa-reactive")
2. アプリケーションの記述
-
次のいずれかの方法を使用して、API エンドポイントを保護し、アプリケーションにアクセスできるユーザーを決定します。
-
すべてのユーザーがアプリケーションにアクセスできるように、
/api/publicエンドポイントを実装します。 次のコードスニペットに示すように、通常の Jakarta REST リソースを 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"; } } -
Implement the
/api/publicendpoint to allow all users to access the application. The source code for the/api/adminendpoint is similar, but instead, you use a@RolesAllowedannotation to ensure that only users granted theadminrole can access the endpoint. Add a Jakarta REST resource with the following@RolesAllowedannotation: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(); } }
-
2.1. ユーザーエンティティーの定義
-
次のコードで説明するように、
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 will be 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();
}
}
security-jpa エクステンションは、単一のエンティティが @UserDefinition でアノテーションされている場合にのみ、初期化されます。
| 1 | アノテーション @UserDefinition は単一のエンティティに存在する必要があり、通常の Hibernate ORM エンティティまたは Panache 付き Hibernate ORM エンティティのいずれかにすることができます。 |
| 2 | ユーザー名に使用するフィールドを示します。 |
| 3 | パスワードに使用するフィールドを示します。 デフォルトでは、bcrypt でハッシュされたパスワードを使用します。 プレーンテキストまたはカスタムパスワードを使用するように設定できます。 |
| 4 | ターゲットプリンシパル表現属性に追加されたロールのコンマ区切りリストを示します。 |
| 5 | 適切な bcrypt ハッシュを使用してパスワードをハッシュ化して、ユーザーを追加できます。 |
Hibernate Reactive Panache uses io.quarkus.hibernate.reactive.panache.PanacheEntity instead of io.quarkus.hibernate.orm.panache.PanacheEntity.
For more information, see User file.
|
2.2. アプリケーションの設定
-
quarkus.http.auth.basicプロパティーをtrueに設定して、組み込みの Quarkus Basic 認証 メカニズムを有効にします。quarkus.http.auth.basic=trueWhen secure access is required and no other authentication mechanisms are enabled, the built-in Basic authentication of Quarkus is the fallback authentication mechanism. Therefore, in this tutorial, you do not need to set the property
quarkus.http.auth.basictotrue. -
Configure at least one data source in the
application.propertiesfile so thesecurity-jpaextension can access your database. For example: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クラスを実装します:
|
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");
}
}
上記の例は、指定されたデータベースによってアプリケーションがどのように保護されて ID が提供されるかを示しています。
|
本番環境では、プレーンテキストのパスワードは保存しないでください。 そのため、 |
3. Test your application by using Dev Services for PostgreSQL
アプリケーションを本番モードで実行する前に、Dev Services for PostgreSQL を使用して、JVM モードとネイティブモードでアプリケーションの結合テストを完了します。
-
アプリケーションを dev モードで実行するには、以下を実行します。
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.elytron.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"));
}
}
このコードサンプルからわかるように、テストコードからテストコンテナーを起動する必要はありません。
|
When you start your application in dev mode, |
3.1. 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
3.2. アプリケーションのコンパイルと実行
-
次のいずれかの方法で、Quarkusアプリケーションをコンパイルして実行します:
-
JVMモード
-
アプリケーションをコンパイルします。
CLIquarkus buildMaven./mvnw installGradle./gradlew build -
アプリケーションを実行します。
java -jar target/quarkus-app/quarkus-run.jar
-
-
ネイティブモード
-
アプリケーションをコンパイルします。
CLIquarkus build --nativeMaven./mvnw install -DnativeGradle./gradlew build -Dquarkus.package.type=native -
アプリケーションを実行します。
./target/security-jpa-quickstart-runner
-
-
3.3. アプリケーションのセキュリティへのアクセスおよびテスト
アプリケーションの実行中に、以下の 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 にアクセスすることもできます。
|
If you use a browser to anonymously connect to a protected resource, a Basic authentication form displays, prompting you to enter credentials. |
3.4. 結果
認可されたユーザーのクレデンシャル(例えば、 admin:admin )を提供すると、Jakarta Persistenceセキュリティエクステンションは、認証してそのユーザーのロールをロードします。 admin ユーザーは、保護されたリソースへのアクセスが認可されます。
If a resource is protected with @RolesAllowed("user"), the user admin is not authorized to access the resource because it is not assigned to the "user" role, as shown in the following shell example:
$ 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
Finally, the user named user is authorized and the security context contains the principal details, for example, the username.
$ 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
Congratulations! You have learned how to create and test a secure Quarkus application by combining the built-in Basic authentication in Quarkus with the Jakarta Persistence identity provider.
このチュートリアルの完了後、Quarkus のより高度なセキュリティーメカニズムについて学習できます。
以下の情報は、Quarkus エンドポイントへの安全なシングルサインオンアクセスに OpenID Connect を使用する方法を示しています。