The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Getting started with Security by using Basic authentication and Jakarta Persistence

Get started with Quarkus Security by securing your Quarkus application endpoints with the built-in Quarkus Basic authentication and the Jakarta Persistence identity provider, enabling role-based access control.

The Jakarta Persistence IdentityProvider verifies and converts a Basic authentication user name and password pair to a SecurityIdentity instance, which is used to authorize access requests, making your Quarkus application secure.

For more information about Jakarta Persistence, see the Quarkus Security with Jakarta Persistence guide.

This tutorial prepares you to implement more advanced security mechanisms in Quarkus, for example, how to use the OpenID Connect (OIDC) authentication mechanism.

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 17+がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.9.6

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

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

Building your application

This tutorial gives detailed steps for creating an application with endpoints that illustrate various authorization policies:

エンドポイント 説明

/api/public

Accessible without authentication, this endpoint allows anonymous access.

/api/admin

Secured with role-based access control (RBAC), this endpoint is accessible only to users with the admin role. Access is controlled declaratively by using the @RolesAllowed annotation.

/api/users/me

Also secured by RBAC, this endpoint is accessible only to users with the user role. It returns the caller’s username as a string.

To examine the completed example, download the archive or clone the Git repository:

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

You can find the solution in the security-jpa-quickstart directory.

1. Create and verify the Maven project

For Quarkus Security to be able to map your security source to Jakarta Persistence entities, ensure that the Maven project in this tutorial includes the security-jpa or security-jpa-reactive extension.

Hibernate ORM with Panache is used to store your user identities, but you can also use Hibernate ORM with the security-jpa extension. Both Hibernate Reactive and Hibernate Reactive with Panache can be used with the security-jpa-reactive extension.

You must also add your preferred database connector library. The instructions in this example tutorial use a PostgreSQL database for the identity store.

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

You can create a new Maven project with the Security Jakarta Persistence extension or add the extension to an existing Maven project. You can use either Hibernate ORM or Hibernate Reactive.

  • To create a new Maven project with the Jakarta Persistence extension, complete one of the following steps:

    • To create the Maven project with Hibernate ORM, use the following command:

コマンドラインインタフェース
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.9.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"

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

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

        コマンドラインインタフェース
        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 エクステンションを追加するには、プロジェクトのベース・ディレクトリーから以下のコマンドを実行します:

        コマンドラインインタフェース
        quarkus extension add security-jpa-reactive
        Maven
        ./mvnw quarkus:add-extension -Dextensions='security-jpa-reactive'
        Gradle
        ./gradlew addExtension --extensions='security-jpa-reactive'

1.2. Verify the quarkus-security-jpa dependency

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-jpa extension, check for the following configuration:

    pom.xml
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-security-jpa</artifactId>
    </dependency>
    build.gradle
    implementation("io.quarkus:quarkus-security-jpa")
  • To verify the security-jpa-reactive extension, check for the following configuration:

    pom.xml
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-security-jpa-reactive</artifactId>
    </dependency>
    build.gradle
    implementation("io.quarkus:quarkus-security-jpa-reactive")

2. アプリケーションの記述

  • Secure the API endpoint to determine who can access the application by using one of the following approaches:

    • Implement the /api/public endpoint to allow all users access to the application. Add a regular Jakarta REST resource to your Java source code, as shown in the following code snippet:

      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 an /api/admin endpoint that can only be accessed by users who have the admin role. The source code for the /api/admin endpoint is similar, but instead, you use a @RolesAllowed annotation to ensure that only users granted the admin role can access the endpoint. Add a Jakarta REST resource with the following @RolesAllowed annotation:

      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";
          }
      }
    • Implement an /api/users/me endpoint that can only be accessed by users who have the user role. Use SecurityContext to get access to the currently authenticated Principal user and to return their username, all of which is retrieved from the database.

      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();
          }
      }

3. ユーザーエンティティーの定義

  • 次のコードで説明するように、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();
    }
}

The security-jpa extension only initializes if a single entity is annotated with @UserDefinition.

1 The @UserDefinition annotation must be present on a single entity, either a regular Hibernate ORM entity or a Hibernate ORM with Panache entity.
2 ユーザー名に使用するフィールドを示します。
3 Indicates the field used for the password. By default, it uses bcrypt-hashed passwords. You can configure it to use plain text or custom passwords.
4 Indicates the comma-separated list of roles added to the target principal representation attributes.
5 Allows us to add users while hashing passwords with the proper bcrypt hash.

Don’t forget to set up the Panache and PostgreSQL JDBC driver, please see Setting up and configuring Hibernate ORM with Panache for more information.

Hibernate Reactive Panache uses io.quarkus.hibernate.reactive.panache.PanacheEntity instead of io.quarkus.hibernate.orm.panache.PanacheEntity. For more information, see User file.

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

  1. Enable the built-in Quarkus Basic authentication mechanism by setting the quarkus.http.auth.basic property to true:

    quarkus.http.auth.basic=true

    When 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.basic to true.

  2. Configure at least one data source in the application.properties file so the security-jpa extension 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
  3. データベースをユーザーとロールで初期化するには、次のコードで説明するように Startup クラスを実装します:

  • The URLs of Reactive datasources that are used by the security-jpa-reactive extension are set with the quarkus.datasource.reactive.url configuration property and not the quarkus.datasource.jdbc.url configuration property typically used by JDBC datasources.

    %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 is not recommended for production. Therefore, adjustments are needed in a production environment.

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");
    }
}

The preceding example demonstrates how the application can be protected and identities provided by the specified database.

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

5. Test your application by using Dev Services for PostgreSQL

Complete the integration testing of your application in JVM and native modes by using Dev Services for PostgreSQL before you run your application in production mode.

テストプロジェクトに以下の依存関係を追加することから始めます。

pom.xml
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <scope>test</scope>
</dependency>
build.gradle
testImplementation("io.rest-assured:rest-assured")
  • To run your application in dev mode:

コマンドラインインタフェース
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev
  • The following properties configuration demonstrates how to enable PostgreSQL testing to run only in production (prod) mode. In this scenario, Dev Services for PostgreSQL launches and configures a PostgreSQL test container.

%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"));
    }
}

このコードサンプルからわかるように、テストコードからテストコンテナーを起動する必要はありません。

When you start your application in dev mode, Dev Services for PostgreSQL launches a PostgreSQL dev mode container so that you can start developing your application. While developing your application, you can add and run tests individually by using the Continuous Testing feature. Dev Services for PostgreSQL supports testing while you develop by providing a separate PostgreSQL test container that does not conflict with the dev mode container.

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

5.2. アプリケーションのコンパイルと実行

  • 次のいずれかの方法で、Quarkusアプリケーションをコンパイルして実行します:

    • JVM モード

      1. アプリケーションをコンパイルします:

        コマンドラインインタフェース
        quarkus build
        Maven
        ./mvnw install
        Gradle
        ./gradlew build
      2. アプリケーションの実行:

        java -jar target/quarkus-app/quarkus-run.jar
    • ネイティブモード

      1. アプリケーションをコンパイルします:

        コマンドラインインタフェース
        quarkus build --native
        Maven
        ./mvnw install -Dnative
        Gradle
        ./gradlew build -Dquarkus.package.type=native
      2. アプリケーションの実行:

        ./target/security-jpa-quickstart-1.0.0-SNAPSHOT-runner

5.3. アプリケーションのセキュリティへのアクセスおよびテスト

When your application is running, you can access its endpoints by using one of the following Curl commands.

  • 保護されたリソースを匿名で攻撃してみましょう:

    $ 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

You can also access the same endpoint URLs by using a browser.

If you use a browser to connect to a protected resource anonymously, a Basic authentication form displays, prompting you to enter credentials.

5.4. 結果

When you provide the credentials of an authorized user, for example, admin:admin, the Jakarta Persistence security extension authenticates and loads the user’s roles. The admin user is authorized to access the protected resources.

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

次のステップ

You have successfully learned how to create and test a secure Quarkus application. This was achieved by integrating the built-in Basic authentication in Quarkus with the Jakarta Persistence identity provider.

After completing this tutorial, you can explore more advanced security mechanisms in Quarkus. The following information shows you how to use OpenID Connect for secure single sign-on access to your Quarkus endpoints:

関連コンテンツ