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

JDBCと共にSecurityを使用

このガイドでは、Quarkusアプリケーションがデータベースを使用してユーザーIDを保存する方法を説明します。

前提条件

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

  • 約15分

  • IDE

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

  • Apache Maven 3.9.6

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

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

アーキテクチャ

この例では、3つのエンドポイントを提供する非常にシンプルなマイクロサービスを構築します:

  • /api/public

  • /api/users/me

  • /api/admin

/api/public エンドポイントは匿名でアクセスできます。 /api/admin エンドポイントは RBAC (Role-Based Access Control) で保護されており、 admin の役割を与えられたユーザーのみがアクセスできます。このエンドポイントでは、 @RolesAllowed アノテーションを使用して、アクセス制約を宣言的に強制します。 /api/users/me エンドポイントも RBAC (Role-Based Access Control) で保護されており、 user ロールで付与されたユーザーのみがアクセスできます。レスポンスとして、ユーザーに関する詳細を含むJSONドキュメントを返します。

ソリューション

次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。

Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.gitアーカイブ をダウンロードします。

ソリューションは security-jdbc-quickstart ディレクトリ にあります。

Mavenプロジェクトの作成

まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。

コマンドラインインタフェース
quarkus create app org.acme:security-jdbc-quickstart \
    --extension='elytron-security-jdbc,jdbc-postgresql,resteasy-reactive' \
    --no-code
cd security-jdbc-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストールと使用方法の詳細については、 Quarkus CLI ガイドを参照してください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.8.3:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=security-jdbc-quickstart \
    -Dextensions='elytron-security-jdbc,jdbc-postgresql,resteasy-reactive' \
    -DnoCode
cd security-jdbc-quickstart

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

Windowsユーザーの場合:

  • cmdを使用する場合、(バックスラッシュ \ を使用せず、すべてを同じ行に書かないでください)。

  • Powershellを使用する場合は、 -D パラメータを二重引用符で囲んでください。例: "-DprojectArtifactId=security-jdbc-quickstart"

選択したデータベースコネクタライブラリを追加することを忘れないでください。ここでは、PostgreSQLをIDストアとして使用しています。

このコマンドは新しいプロジェクトを生成し、 elytron-security-jdbc エクステンションをインポートします。これはQuarkusアプリケーション用の wildfly-elytron-realm-jdbc アダプターです。

すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリーで以下のコマンドを実行することで、プロジェクトに elytron-security-jdbc エクステンションを追加することができます:

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

これにより、ビルドファイルに以下が追加されます:

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

アプリケーションの記述

まず、 /api/public のエンドポイントを実装することから始めましょう。以下のソースコードからわかるように、これは通常のJakarta RESTリソースに過ぎません:

package org.acme.security.jdbc;

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 エンドポイントのソースコードも非常にシンプルです。ここでの主な違いは、 admin ロールで付与されたユーザーだけがエンドポイントにアクセスできるように @RolesAllowed アノテーションを使用していることです:

package org.acme.security.jdbc;

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

最後に、 /api/users/me エンドポイントを考えてみましょう。下のソースコードを見ればわかるように、 user のロールを持つユーザのみを信頼しています。現在の認証されているプリンシパルへのアクセスを得るために SecurityContext を使用しており、ユーザーの名前を返します。この情報はデータベースから読み込まれます。

package org.acme.security.jdbc;

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

アプリケーションの設定

elytron-security-jdbc エクステンションは、データベースにアクセスするために少なくとも一つのデータソースが必要です。

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=quarkus
quarkus.datasource.password=quarkus
quarkus.datasource.jdbc.url=jdbc:postgresql:elytron-security-jdbc

In our context, we are using PostgreSQL as identity store, and we initialize the database with users and roles. We will use the salted and hashed version of password as a password in this example. We can use the BcryptUtil class to generate passwords in the Modular Crypt Format (MCF).

CREATE TABLE test_user (
  id INT,
  username VARCHAR(255),
  password VARCHAR(255),
  role VARCHAR(255)
);

INSERT INTO test_user (id, username, password, role) VALUES (1, 'admin', '$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'admin');
INSERT INTO test_user (id, username, password, role) VALUES (2, 'user','$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'user');

When signing up new users, we can encrypt their password as follows:

package org.acme.security.jdbc;

import io.quarkus.elytron.security.common.BcryptUtil;

public class AccountService {

    public void signupUser(String username, String password) {
        String encryptedPassword = BcryptUtil.bcryptHash(password);

        // store user with the encrypted password in the database
    }
}

これで、Elytron JDBC Realmを設定することができます。

quarkus.security.jdbc.enabled=true
quarkus.security.jdbc.principal-query.sql=SELECT u.password, u.role FROM test_user u WHERE u.username=? (1)
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.enabled=true (2)
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.password-index=1
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.salt-index=-1
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.iteration-count-index=-1
quarkus.security.jdbc.principal-query.attribute-mappings.0.index=2 (3)
quarkus.security.jdbc.principal-query.attribute-mappings.0.to=groups

elytron-security-jdbc エクステンションは、ユーザーとそのアイデンティティを認証するために、少なくとも一つのプリンシパルのクエリを必要とします。

1 ユーザーのパスワードと、ロードしたい追加情報を返却するパラメーター化されたSQL文(単一のパラメーター付)を定義しています。
2 We configure the password mapper with the position of the password field in the SELECT fields and other information like salt, hash encoding, etc. Setting the salt and iteration count indexes to -1 is required for MCF.
3 attribute-mappings を使用して、 SELECT の射影フィールド(例:ここでは u.role )をターゲットの Principal 表現の属性にバインドしています。

principal-query の設定では、 index のプロパティーはすべて 1 から始まります (0 ではなく)。

アプリケーションのテスト

アプリケーションが保護され、アイデンティティがデータベースから提供されるようになりました。非常に最初に確認しなければならないことは、匿名アクセスが機能することを確認することです。

$ 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

Not authorized%

ここまでは順調ですが、今度は許可されたユーザーで試してみましょう。

$ curl -i -X GET -u admin:password http://localhost:8080/api/admin
HTTP/1.1 200 OK
Content-Length: 5
Content-Type: text/plain;charset=UTF-8

admin%

By providing the admin:password credentials, the extension authenticated the user and loaded their roles. The admin user is authorized to access to the protected resources.

ユーザー admin は、この役割を持っていないので、 @RolesAllowed("user") で保護されたリソースへのアクセスを禁止する必要があります。

$ curl -i -X GET -u admin:password http://localhost:8080/api/users/me
HTTP/1.1 403 Forbidden
Content-Length: 34
Content-Type: text/html;charset=UTF-8

Forbidden%

最後に、ユーザー user を使用すると動作し、セキュリティーコンテキストには主要な詳細(例えばユーザー名)が含まれています。

$ curl -i -X GET -u user:password http://localhost:8080/api/users/me
HTTP/1.1 200 OK
Content-Length: 4
Content-Type: text/plain;charset=UTF-8

user%

高度な設定

このガイドでは、簡単な使用例のみを取り上げていますが、このエクステンションは複数のデータソース、複数のプリンシパルクエリの設定、および bcrypt パスワードマッパーを提供しています。

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=quarkus
quarkus.datasource.password=quarkus
quarkus.datasource.jdbc.url=jdbc:postgresql:multiple-data-sources-users

quarkus.datasource.permissions.db-kind=postgresql
quarkus.datasource.permissions.username=quarkus
quarkus.datasource.permissions.password=quarkus
quarkus.datasource.permissions.jdbc.url=jdbc:postgresql:multiple-data-sources-permissions

quarkus.security.jdbc.enabled=true
quarkus.security.jdbc.principal-query.sql=SELECT u.password FROM test_user u WHERE u.username=?
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.enabled=true
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.password-index=1
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.salt-index=-1
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.iteration-count-index=-1

quarkus.security.jdbc.principal-query.roles.sql=SELECT r.role_name FROM test_role r, test_user_role ur WHERE ur.username=? AND ur.role_id = r.id
quarkus.security.jdbc.principal-query.roles.datasource=permissions
quarkus.security.jdbc.principal-query.roles.attribute-mappings.0.index=1
quarkus.security.jdbc.principal-query.roles.attribute-mappings.0.to=groups

設定リファレンス

ビルド時に固定される構成プロパティ - 他のすべての構成プロパティは実行時にオーバーライド可能

Configuration property

デフォルト

The realm name

Environment variable: QUARKUS_SECURITY_JDBC_REALM_NAME

Show more

string

Quarkus

If the properties store is enabled.

Environment variable: QUARKUS_SECURITY_JDBC_ENABLED

Show more

boolean

false

The sql query to find the password

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_SQL

Show more

string

The data source to use

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_DATASOURCE

Show more

string

If the clear-password-mapper is enabled.

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_CLEAR_PASSWORD_MAPPER_ENABLED

Show more

boolean

false

The index (1 based numbering) of the column containing the clear password

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_CLEAR_PASSWORD_MAPPER_PASSWORD_INDEX

Show more

int

1

If the bcrypt-password-mapper is enabled.

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_BCRYPT_PASSWORD_MAPPER_ENABLED

Show more

boolean

false

The index (1 based numbering) of the column containing the password hash

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_BCRYPT_PASSWORD_MAPPER_PASSWORD_INDEX

Show more

int

0

A string referencing the password hash encoding ("BASE64" or "HEX")

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_BCRYPT_PASSWORD_MAPPER_HASH_ENCODING

Show more

base64, hex

base64

The index (1 based numbering) of the column containing the Bcrypt salt

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_BCRYPT_PASSWORD_MAPPER_SALT_INDEX

Show more

int

0

A string referencing the salt encoding ("BASE64" or "HEX")

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_BCRYPT_PASSWORD_MAPPER_SALT_ENCODING

Show more

base64, hex

base64

The index (1 based numbering) of the column containing the Bcrypt iteration count

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_BCRYPT_PASSWORD_MAPPER_ITERATION_COUNT_INDEX

Show more

int

0

The index (1 based numbering) of column to map

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__INDEX

Show more

int

0

The target attribute name

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY_ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__TO

Show more

string

required

The sql query to find the password

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__SQL

Show more

string

The data source to use

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__DATASOURCE

Show more

string

The index (1 based numbering) of column to map

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__INDEX

Show more

int

0

The target attribute name

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__TO

Show more

string

required

If the clear-password-mapper is enabled.

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__CLEAR_PASSWORD_MAPPER_ENABLED

Show more

boolean

false

The index (1 based numbering) of the column containing the clear password

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__CLEAR_PASSWORD_MAPPER_PASSWORD_INDEX

Show more

int

1

If the bcrypt-password-mapper is enabled.

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__BCRYPT_PASSWORD_MAPPER_ENABLED

Show more

boolean

false

The index (1 based numbering) of the column containing the password hash

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__BCRYPT_PASSWORD_MAPPER_PASSWORD_INDEX

Show more

int

0

A string referencing the password hash encoding ("BASE64" or "HEX")

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__BCRYPT_PASSWORD_MAPPER_HASH_ENCODING

Show more

base64, hex

base64

The index (1 based numbering) of the column containing the Bcrypt salt

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__BCRYPT_PASSWORD_MAPPER_SALT_INDEX

Show more

int

0

A string referencing the salt encoding ("BASE64" or "HEX")

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__BCRYPT_PASSWORD_MAPPER_SALT_ENCODING

Show more

base64, hex

base64

The index (1 based numbering) of the column containing the Bcrypt iteration count

Environment variable: QUARKUS_SECURITY_JDBC_PRINCIPAL_QUERY__NAMED_PRINCIPAL_QUERIES__BCRYPT_PASSWORD_MAPPER_ITERATION_COUNT_INDEX

Show more

int

0

関連コンテンツ