Basic 認証と Jakarta Persistence を使用したセキュリティーの開始
Quarkus の組み込み Basic 認証 と Jakarta Persistence ID プロバイダーで Quarkus アプリケーションのエンドポイントを保護し、ロールベースのアクセス制御を有効にして、Quarkus Security を使い始めます。
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.15
-
使用したい場合は、 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 エクステンションが含まれていることを確認してください。
|
ユーザー ID の保存には 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-jpaMaven./mvnw quarkus:add-extension -Dextensions='security-jpa'Gradle./gradlew addExtension --extensions='security-jpa' -
Security Jakarta Persistence エクステンションを、Hibernate Reactive を使用する既存の Maven プロジェクトに追加するには、プロジェクトのベース・ディレクトリーから以下のコマンドを実行します。
CLIquarkus extension add security-jpa-reactiveMaven./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.javapackage 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.javapackage 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.javapackage 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.javapackage 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 | 適切にハッシュ化されたパスワードでユーザーを追加するためのヘルパーメソッドを提供します。 BcryptUtil.bcryptHash() メソッドは以下を行います。
|
|
パスワードハッシュのベストプラクティス:
|
-
パスワードとロールの設定に関する詳細については、 アプリケーションの設定 を参照してください。
-
パスワードのハッシュ化と利用可能なオプションに関する詳細については、 パスワードの保存とハッシュ化 を参照してください。
|
Panache と PostgreSQL JDBC ドライバーをセットアップしてください。詳細については、Panache を使用した Hibernate ORM のセットアップと設定 を参照してください。 |
|
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.javapackage 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")
アプリケーションを開発モードで実行するには:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
このシナリオでは、Dev Services for PostgreSQL が PostgreSQL テストコンテナーを起動して構成します。
Podman または Docker のいずれかがコンピューターにインストールされていることを確認してください。
統合テストを記述するには、次のコードサンプルを使用します。
src/test/java/org/acme/security/jpa/JpaSecurityRealmTest.javapackage 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 オプションを選択します。
|
アプリケーションを開発モードで起動すると、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 docker.io/library/postgres:18
6.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.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 を使用する方法を示しています。