JDBC と Security の使用
このガイドでは、Quarkus アプリケーションがデータベースを使用してユーザー ID を保存する方法を説明します。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOMEが適切に設定されていること -
Apache Maven 3.9.12
-
使用したい場合は、 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 プロジェクトの作成
まず、新しいプロジェクトが必要です。次のコマンドで新しいプロジェクトを作成します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-Dパラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=security-jdbc-quickstart"
|
選択したデータベースコネクターライブラリーを追加することを忘れないでください。ここでは、PostgreSQL を ID ストアとして使用しています。 |
このコマンドは新しいプロジェクトを生成し、Quarkus アプリケーション用の wildfly-elytron-realm-jdbc アダプターである elytron-security-jdbc エクステンションをインポートします。
すでに Quarkus プロジェクトが設定されている場合は、プロジェクトのベースディレクトリーで次のコマンドを実行することで、プロジェクトに elytron-security-jdbc エクステンションを追加できます。
quarkus extension add elytron-security-jdbc
./mvnw quarkus:add-extension -Dextensions='elytron-security-jdbc'
./gradlew addExtension --extensions='elytron-security-jdbc'
これにより、ビルドファイルに以下が追加されます。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security-jdbc</artifactId>
</dependency>
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 を使用して現在認証されている Principal にアクセスし、ユーザー名を返します。この情報はデータベースからロードされます。
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 エクステンションは、データベースにアクセスするために少なくとも 1 つのデータソースが必要です。
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=quarkus
quarkus.datasource.password=quarkus
quarkus.datasource.jdbc.url=jdbc:postgresql:elytron-security-jdbc
ここでは PostgreSQL を ID ストアとして使用しており、ユーザーとロールでデータベースを初期化します。この例では、パスワードとして password のソルト化およびハッシュ化されたバージョンを使用します。 BcryptUtil クラスを使用して、モジュラークリプトフォーマット (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');
新規ユーザーを登録する際、以下のようにパスワードを暗号化できます。
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.attribute-mappings.0.index=2 (3)
quarkus.security.jdbc.principal-query.attribute-mappings.0.to=groups
elytron-security-jdbc エクステンションは、ユーザーとその ID を認証するために、少なくとも 1 つのプリンシパルクエリを必要とします。
| 1 | ユーザーのパスワードと、ロードしたい追加情報を返すパラメーター化された SQL ステートメント (正確に 1 つのパラメーターを持つ) を定義します。 |
| 2 | パスワードマッパーは、 SELECT フィールドにおけるパスワードフィールドの位置で設定されます。ソルトおよび反復回数のインデックスはデフォルトで -1 に設定されているため、ハッシュはモジュラークリプトフォーマット (MCF) に格納されます。各要素を 3 つの個別のカラムに分解するために、これらの設定をオーバーライドできます。 |
| 3 | attribute-mappings を使用して、 SELECT 射影フィールド (ここでは u.role ) をターゲットの Principal 表現属性にバインドします。 |
|
|
アプリケーションのテスト
これでアプリケーションが保護され、ID はデータベースから提供されます。最初に確認すべきことは、匿名アクセスが機能することを確認することです。
$ 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%
admin:password クレデンシャルを提供することで、エクステンションはユーザーを認証し、そのロールをロードしました。 admin ユーザーは保護されたリソースへのアクセスが認可されます。
ユーザー 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.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: Show more |
string |
|
If the properties store is enabled. Environment variable: Show more |
boolean |
|
The sql query to find the password Environment variable: Show more |
string |
|
The data source to use Environment variable: Show more |
string |
|
If the clear-password-mapper is enabled. Environment variable: Show more |
boolean |
|
The index (1 based numbering) of the column containing the clear password Environment variable: Show more |
int |
|
If the bcrypt-password-mapper is enabled. Environment variable: Show more |
boolean |
|
The index (1 based numbering) of the column containing the password hash Environment variable: Show more |
int |
|
A string referencing the password hash encoding ("BASE64" or "HEX") Environment variable: Show more |
|
|
The index (1 based numbering) of the column containing the Bcrypt salt. The default value of Environment variable: Show more |
int |
|
A string referencing the salt encoding ("BASE64" or "HEX") Environment variable: Show more |
|
|
The index (1 based numbering) of the column containing the Bcrypt iteration count. The default value of Environment variable: Show more |
int |
|
The index (1 based numbering) of column to map Environment variable: Show more |
int |
|
The target attribute name Environment variable: Show more |
string |
required |
型 |
デフォルト |
|
The sql query to find the password Environment variable: Show more |
string |
|
The data source to use Environment variable: Show more |
string |
|
The index (1 based numbering) of column to map Environment variable: Show more |
int |
|
The target attribute name Environment variable: Show more |
string |
required |
If the clear-password-mapper is enabled. Environment variable: Show more |
boolean |
|
The index (1 based numbering) of the column containing the clear password Environment variable: Show more |
int |
|
If the bcrypt-password-mapper is enabled. Environment variable: Show more |
boolean |
|
The index (1 based numbering) of the column containing the password hash Environment variable: Show more |
int |
|
A string referencing the password hash encoding ("BASE64" or "HEX") Environment variable: Show more |
|
|
The index (1 based numbering) of the column containing the Bcrypt salt. The default value of Environment variable: Show more |
int |
|
A string referencing the salt encoding ("BASE64" or "HEX") Environment variable: Show more |
|
|
The index (1 based numbering) of the column containing the Bcrypt iteration count. The default value of Environment variable: Show more |
int |
|