Basic認証とJakarta Persistenceを使用したセキュリティの入門
Quarkus Security を使い始めるには、Quarkus アプリケーションのエンドポイントを組み込みの Quarkus の Basic 認証 と Jakarta Persistence の ID プロバイダーでセキュアにし、ロールベースアクセス制御を有効にします。
Jakarta Persistence の IdentityProvider
は、Basic 認証 のユーザー名とパスワードのペアを検証して SecurityIdentity
インスタンスに変換します。このインスタンスはアクセス要求を承認するために使用され、Quarkus アプリケーションを安全にします。
Jakarta Persistence の詳細は、Quarkus Security と Jakarta Persistence ガイドを参照してください。
このチュートリアルでは、OpenID Connect (OIDC) 認証メカニズムの使用方法など、Quarkus でより高度なセキュリティーメカニズムを実装するための準備を行います。
要件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
アプリケーションのビルド
このチュートリアルでは、さまざまな認可ポリシーを示すエンドポイントを使用してアプリケーションを作成する詳細な手順を示します。
エンドポイント | 説明 |
---|---|
|
このエンドポイントは認証なしでアクセス可能で、匿名アクセスを使用できます。 |
|
ロールベースのアクセス制御 (RBAC) で保護されたこのエンドポイントには、 |
|
このエンドポイントも RBAC によって保護されており、 |
完了した例を確認するには、https://github.com/quarkusio/quarkus-quickstarts/archive/main.zip[アーカイブ] をダウンロードするか、Git リポジトリーのクローンを作成します。
解決策は、 |
1. Maven プロジェクトの作成および検証
Quarkus Security がセキュリティーソースを Jakarta Persistence エンティティーにマップできるようにするには、このチュートリアルの Maven プロジェクトに quarkus-security-jpa
または quarkus-security-jpa-reactive
エクステンションが含まれていることを確認してください。
Hibernate ORM と Panache は、ユーザーアイデンティティーを保存するために使用されますが、 Hibernate Reactive と Hibernate Reactive と Panache はどちらも 優先するデータベースコネクターライブラリーも追加する必要があります。 このサンプルチュートリアルの手順では、アイデンティティーストアに PostgreSQL データベースを使用します。 |
1.1. Maven プロジェクトの作成
Security Jakarta Persistence エクステンションを使用して新しい Maven プロジェクトを作成したり、既存の Maven プロジェクトにエクステンションを追加したりできます。 Hibernate ORM または Hibernate Reactive のいずれかを使用できます。
1.1.1. 新しい Maven プロジェクトの作成
-
Jakarta Persistence エクステンションで新しい Maven プロジェクトを作成するには、以下のいずれかの手順を実行します。
-
Hibernate ORM を使用して Maven プロジェクトを作成するには、次のコマンドを使用します。
-
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=security-jpa-quickstart"
1.1.2. 既存のプロジェクトに Jakarta Persistence エクステンションを追加する
-
既存の 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 依存関係の確認
上記のいずれかのコマンドを実行して Maven プロジェクトを作成した後、 quarkus-security-jpa
依存関係がプロジェクトビルド XML ファイルに追加されたことを確認します。
-
quarkus-security-jpa
エクステンションを確認するには、以下の設定を確認します。pom.xml<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-security-jpa</artifactId> </dependency>
build.gradleimplementation("io.quarkus:quarkus-security-jpa")
-
quarkus-security-jpa-reactive
エクステンションを確認するには、次の設定を確認します。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 ソースコードに追加します。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"; } }
-
管理者ロールを持つユーザーのみがアクセスできる /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();
}
}
quarkus-security-jpa
エクステンションは、単一のエンティティーに @UserDefinition
アノテーションが付けられている場合にのみ初期化されます。
1 | @UserDefinition アノテーションは、通常の Hibernate ORM エンティティーまたは Panache エンティティーを含む Hibernate ORM のいずれかの単一のエンティティーに存在する必要があります。 |
2 | ユーザー名に使用するフィールドを示します。 |
3 | パスワードに使用するフィールドを示します。 デフォルトでは、bcrypt でハッシュされたパスワードを使用します。 プレーンテキストまたはカスタムパスワードを使用するように設定できます。 |
4 | ターゲットプリンシパル表現属性に追加されたロールのコンマ区切りリストを示します。 |
5 | Provides a helper method to add users with properly hashed passwords. The BcryptUtil.bcryptHash() method:
|
Password hashing best practices:
|
-
For more information about configuring passwords and roles, see Configure the application.
-
For more information on hashing passwords and available options, see Password storage and hashing.
Remember to set up the Panache and PostgreSQL JDBC driver. For more information, see Setting up and configuring Hibernate ORM with Panache. |
Hibernate Reactive Panache は、 |
4. アプリケーションの設定
-
quarkus.http.auth.basic
プロパティーをtrue
に設定して、組み込みの Quarkus Basic 認証 メカニズムを有効にします。quarkus.http.auth.basic=true
安全なアクセスが必要で、他の認証メカニズムが有効になっていない場合、Quarkus に組み込まれている Basic 認証 がフォールバック認証メカニズムとなります。 したがって、このチュートリアルでは、プロパティー
quarkus.http.auth.basic
をtrue
に設定する必要はありません。 -
quarkus-security-jpa
エクステンションがデータベースにアクセスできるように、src/main/resources/application.properties
ファイルで少なくとも 1 つのデータソースを設定します。 例:src/main/resources/application.propertiesquarkus.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.
プロファイル接頭辞を追加することで、データソースプロパティーが実稼働モードで実行されているアプリケーションによってのみ監視されるようになります。 -
データベースをユーザーとロールで初期化するには、次のコードで説明するように
Startup
クラスを実装します:
|
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");
}
}
上記の例は、指定されたデータベースによってアプリケーションがどのように保護されて ID が提供されるかを示しています。
実稼働環境では、プレーンテキストのパスワードは保存しないでください。
そのため、 |
5. PostgreSQL 用 Dev Services を使用して開発モードでアプリケーションをテストします。
アプリケーションを本番モードで実行する前に、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")
アプリケーションを dev モードで実行するには、以下を実行します。
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
このシナリオでは、 Dev Services for PostgreSQL
が PostgreSQL
テスト・コンテナを起動し、構成します。 Podman
または Docker
のいずれかがコンピュータにインストールされていることを確認します。
統合テストを書くには、次のコードサンプルを使用します:
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"));
}
}
このコードサンプルからわかるように、テストコードからテストコンテナーを起動する必要はありません。
これらのテストを実行するには、アプリケーションを dev モードで起動した後にコンソールに表示される Press [r] to resume testing
オプションを選択します。
アプリケーションを開発モードで起動すると、Dev Services for PostgreSQL が PostgreSQL の開発モードコンテナーを起動し、アプリケーションの開発を開始することができるようになります。 アプリケーションの開発中に、継続的テスト の機能を使って、テストを個別に追加して実行できます。 Dev Services for PostgreSQL は、開発モードコンテナーと競合しない独立した 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 モード
-
アプリケーションをコンパイルします。
CLIquarkus build
Maven./mvnw install
Gradle./gradlew build
-
アプリケーションを実行します。
java -jar target/quarkus-app/quarkus-run.jar
-
-
ネイティブモード
-
アプリケーションをコンパイルします。
CLIquarkus build --native
Maven./mvnw install -Dnative
Gradle./gradlew build -Dquarkus.native.enabled=true
-
アプリケーションを実行します。
./target/security-jpa-quickstart-1.0.0-SNAPSHOT-runner
-
-
6.3. Curl を使用してアプリケーションのセキュリティーにアクセスし、テストする
アプリケーションの実行中に、以下の 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 WWW-Authenticate: Basic
-
認証されたユーザーとして保護されたエンドポイントに接続します:
$ 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 にアクセスすることもできます。
6.4. ブラウザーでアプリケーションのセキュリティーにアクセスしてテストする
ブラウザーを使用して保護されたリソースに匿名で接続する場合は、Basic 認証フォームが表示され、認証情報の入力を求められます。
6.5. 結果
認可されたユーザーの認証情報 (例: admin:admin
) を提供すると、Jakarta Persistence セキュリティーエクステンションは認証してそのユーザーのロールをロードします。
admin
ユーザーは、保護されたリソースへのアクセスが認可されます。
リソースが @RolesAllowed("user")
で保護されている場合は、次の例に示すように、 admin
ユーザーは "user" ロールに割り当てられていないため、リソースへのアクセスを許可されません。
$ curl -i -X GET -u admin:admin http://localhost:8080/api/users/me
HTTP/1.1 403 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
次のステップ
安全な Quarkus アプリケーションを作成し、テストする方法を学習しました。 これは、Quarkus に組み込まれている Basic 認証 を Jakarta Persistence アイデンティティープロバイダーと統合することによって実現されました。
このチュートリアルの完了後、Quarkus のより高度なセキュリティーメカニズムについて学習できます。
以下の情報は、Quarkus エンドポイントへの安全なシングルサインオンアクセスに OpenID Connect
を使用する方法を示しています。