Getting started with security by using Basic authentication and 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.16

  • 使用したい場合は、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

アプリケーションのビルド

このチュートリアルでは、さまざまな認可ポリシーを示すエンドポイントを使用してアプリケーションを作成する詳細な手順を示します。

エンドポイント 説明

/api/public

このエンドポイントは認証なしでアクセス可能で、匿名アクセスを使用できます。

/api/admin

ロールベースのアクセス制御 (RBAC) で保護されたこのエンドポイントには、 admin ロールを持つユーザーのみがアクセスできます。 アクセスは、 @RolesAllowed アノテーションを使用して宣言的に制御されます。

/api/users/me

このエンドポイントも RBAC によって保護されており、 user ロールを持つユーザーのみがアクセスできます。 呼び出し元のユーザー名を文字列として返します。

完了した例を確認するには、https://github.com/quarkusio/quarkus-quickstarts/archive/main.zip[アーカイブ] をダウンロードするか、Git リポジトリーのクローンを作成します。

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

解決策は、 security-jpa-quickstart ディレクトリー にあります。

1. Maven プロジェクトの作成および検証

Quarkus Security がセキュリティーソースを Jakarta Persistence エンティティーにマップできるようにするには、このチュートリアルの Maven プロジェクトに quarkus-security-jpa または quarkus-security-jpa-reactive エクステンションが含まれていることを確認してください。

Hibernate ORM と Panache は、ユーザーアイデンティティーを保存するために使用されますが、 quarkus-security-jpa エクステンションとともに Hibernate ORM を使用することもできます。

Hibernate ReactiveHibernate Reactive と Panache はどちらも quarkus-security-jpa-reactive エクステンションで使用できます。

優先するデータベースコネクターライブラリーも追加する必要があります。 このサンプルチュートリアルの手順では、アイデンティティーストアに PostgreSQL データベースを使用します。

1.1. Maven プロジェクトの作成

Security Jakarta Persistence エクステンションを使用して新しい Maven プロジェクトを作成したり、既存の Maven プロジェクトにエクステンションを追加したりできます。 Hibernate ORM または Hibernate Reactive のいずれかを使用できます。

1.1.1. 新しい Maven プロジェクトの作成

  • Jakarta Persistence エクステンションで新しい Maven プロジェクトを作成するには、以下のいずれかの手順を実行します。

    • Hibernate ORM を使用して Maven プロジェクトを作成するには、次のコマンドを使用します。

コマンドラインインタフェース
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.39.3: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 エクステンションを追加する

  • 既存の Maven プロジェクトに Jakarta Persistence エクステンションを追加するには、次のいずれかの手順を実行します:

    • 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'
    • Hibernate Reactive を使用する既存の Maven プロジェクトに Security Jakarta Persistence エクステンションを追加するには、プロジェクトのベース・ディレクトリーから以下のコマンドを実行します:

      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";
         }
      }
    • 管理者ロールを持つユーザーのみがアクセスできる /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();
    }
}
1 @UserDefinition アノテーションは、通常の Hibernate ORM エンティティまたは Hibernate ORM with Panache エンティティのいずれか、単一のエンティティ上に存在する必要があります。
2 ユーザー名に使用するフィールドを示します。
3 パスワードに使用するフィールドを示します。 デフォルトでは、bcrypt でハッシュされたパスワードを使用します。 プレーンテキストまたはカスタムパスワードを使用するように設定できます。
4 ターゲットプリンシパル表現属性に追加されたロールのコンマ区切りリストを示します。
5 Provides a helper method to add users with bcrypt-hashed passwords. The BcryptUtil.bcryptHash() method:
  • 各パスワードに対してランダムなソルトを自動的に生成します。

  • bcrypt を使用して、10 回のイテレーション (デフォルト) でパスワードをハッシュ化します。

  • アルゴリズム識別子、コストパラメーター、ソルト、およびハッシュを含むモジュラー暗号形式 (MCF) でハッシュを返します。

quarkus-security-jpa エクステンションは、単一のエンティティーに @UserDefinition アノテーションが付けられている場合にのみ初期化されます。

パスワードハッシュのベストプラクティス:

  • 常にプレーンテキストのパスワードを BcryptUtil.bcryptHash() に渡してください。事前にハッシュ化しないでください。

  • bcrypt ハッシュにはソルトが含まれているため、個別のソルトストレージは不要です。

  • 認証時にパスワードを検証するために、フレームワークは BcryptUtil.matches(plainPassword, hashedPassword) を自動的に使用します。

  • カスタムイテレーションの場合: BcryptUtil.bcryptHash(password, iterationCount) を使用します。イテレーション数を高くする (12-14) とセキュリティは向上しますが、パフォーマンスは低下します。

Panache と PostgreSQL JDBC ドライバーをセットアップしてください。詳細については、Panache を使用した Hibernate ORM のセットアップと設定 を参照してください。

Hibernate Reactive Panache は、 io.quarkus.hibernate.orm.panache.PanacheEntity ではなく io.quarkus.hibernate.reactive.panache.PanacheEntity を使用します。 詳細は、ユーザーファイル を参照してください。

4. アプリケーションの設定

  1. quarkus.http.auth.basic プロパティーを true に設定して、組み込みの Quarkus Basic 認証 メカニズムを有効にします。

    quarkus.http.auth.basic=true

    安全なアクセスが必要で、他の認証メカニズムが有効になっていない場合、Quarkus に組み込まれている Basic 認証 がフォールバック認証メカニズムとなります。 したがって、このチュートリアルでは、プロパティー quarkus.http.auth.basictrue に設定する必要はありません。

  2. quarkus-security-jpa エクステンションがデータベースにアクセスできるように、 src/main/resources/application.properties ファイルで少なくとも 1 つのデータソースを設定します。 例:

    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 は、JDBC データソースで通常使用される quarkus.datasource.jdbc.url 設定プロパティーではなく、 quarkus.datasource.reactive.url 設定プロパティーで設定されます。

    src/main/resources/application.properties
    %prod.quarkus.datasource.reactive.url=vertx-reactive:postgresql://localhost:5431/security_jpa
  • In this tutorial, a PostgreSQL database is used for the identity store. Hibernate ORM automatically creates the database schema on startup. This approach is suitable for development but not for production. Therefore, adjustments are needed in a production environment.

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 が提供されるかを示しています。

実稼働環境では、プレーンテキストのパスワードは保存しないでください。 そのため、 quarkus-security-jpa はデフォルトで bcrypt でハッシュ化したパスワードを使用します。

5. PostgreSQL 用 Dev Services を使用して開発モードでアプリケーションをテストします。

アプリケーションを本番モードで実行する前に、Dev Services for PostgreSQL を使用して、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")

アプリケーションを dev モードで実行するには、以下を実行します。

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

このシナリオでは、 Dev Services for PostgreSQLPostgreSQL テスト・コンテナを起動し、構成します。 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 またはブラウザーを使用して、実稼働環境でアプリケーションをテストします。

To test your application using Curl or a browser, start a PostgreSQL server first. Then, compile and run your application in either JVM or native mode.

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モード

      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 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

What’s next

安全な Quarkus アプリケーションを作成し、テストする方法を学習しました。 これは、Quarkus に組み込まれている Basic 認証 を Jakarta Persistence アイデンティティープロバイダーと統合することによって実現されました。

このチュートリアルの完了後、Quarkus のより高度なセキュリティーメカニズムについて学習できます。 以下の情報は、Quarkus エンドポイントへの安全なシングルサインオンアクセスに OpenID Connect を使用する方法を示しています。