JDBCと共にSecurityを使用
このガイドでは、Quarkusアプリケーションがデータベースを使用してユーザーIDを保存する方法を説明します。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.8
-
使用したい場合は、 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ストアとして使用しています。 |
このコマンドは新しいプロジェクトを生成し、 elytron-security-jdbc
エクステンションをインポートします。これはQuarkusアプリケーション用の wildfly-elytron-realm-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
を使用しており、ユーザーの名前を返します。この情報はデータベースから読み込まれます。
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
この例では、PostgreSQLをIDストアとして使用し、ユーザとロールでデータベースを初期化します。
この例では、 password
の塩漬けとハッシュ化したものをパスワードとして使用します。
BcryptUtil
クラスを使用して、MCF(Modular Crypt Format)形式のパスワードを生成することができます。
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
エクステンションは、ユーザーとそのアイデンティティを認証するために、少なくとも一つのプリンシパルのクエリを必要とします。
1 | ユーザーのパスワードと、ロードしたい追加情報を返却するパラメーター化されたSQL文(単一のパラメーター付)を定義しています。 |
2 | The password mapper is configured with the position of the password field in the SELECT fields. The hash is stored in the Modular Crypt Format (MCF) because the salt and iteration count indexes are set to -1 by default. You can override them in order to decompose each element into three separate columns. |
3 | attribute-mappings を使用して、 SELECT の射影フィールド(例:ここでは u.role )をターゲットの Principal 表現の属性にバインドしています。 |
|
アプリケーションのテスト
アプリケーションが保護され、アイデンティティがデータベースから提供されるようになりました。非常に最初に確認しなければならないことは、匿名アクセスが機能することを確認することです。
$ 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 |
|