JDBCでセキュリティーを使用する
このガイドでは、Quarkusアプリケーションがデータベースを使用してユーザーIDを保存する方法を説明します。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 11+ がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.8.1+
-
使用したい場合、 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プロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。
選択したデータベースコネクタライブラリを追加することを忘れないでください。ここでは、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
エンドポイントの実装から始めましょう。以下のソースコードから分かるように、通常のJAX-RSリソースです:
package org.acme.security.jdbc;
import javax.annotation.security.PermitAll;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.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 javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/api/admin")
public class AdminResource {
@GET
@RolesAllowed("admin")
@Produces(MediaType.TEXT_PLAIN)
public String adminResource() {
return "admin";
}
}
Finally, let’s consider the /api/users/me
endpoint. As you can see from the source code below, we are trusting only users with the user
role. We are using SecurityContext
to get access to the current authenticated Principal, and we return the user’s name. This information is loaded from the database.
package org.acme.security.jdbc;
import javax.annotation.security.RolesAllowed;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.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.
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', 'admin', 'admin');
INSERT INTO test_user (id, username, password, role) VALUES (2, 'user','user', 'user');
It is probably useless, but we kindly remind you that you must not store clear-text passwords in production environment ;-). The |
これで、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.clear-password-mapper.enabled=true (2)
quarkus.security.jdbc.principal-query.clear-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 | SELECT フィールドのパスワードフィールドの位置や、ソルト、ハッシュエンコードなどの情報を使って、パスワードマッパーを設定します。 |
3 | We use attribute-mappings to bind the SELECT projection fields (i.e. u.role here) to the target Principal representation attributes. |
|
アプリケーションのテスト
アプリケーションが保護され、アイデンティティがデータベースから提供されるようになりました。非常に最初に確認しなければならないことは、匿名アクセスが機能することを確認することです。
$ 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%
Now, let’s try to hit a protected resource anonymously.
$ 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:admin http://localhost:8080/api/admin
HTTP/1.1 200 OK
Content-Length: 5
Content-Type: text/plain;charset=UTF-8
admin%
admin:admin
資格情報を提供することで、エクステンションはユーザーを認証し、そのロールをロードしました。 admin
ユーザーは、保護されたリソースへのアクセスを許可されています。
ユーザー admin
は、この役割を持っていないので、 @RolesAllowed("user")
で保護されたリソースへのアクセスを禁止する必要があります。
$ 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%
最後に、ユーザー 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%
高度な設定
このガイドでは、簡単な使用例のみを取り上げていますが、このエクステンションは複数のデータソース、複数のプリンシパルクエリの設定、および 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.clear-password-mapper.enabled=true quarkus.security.jdbc.principal-query.clear-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
設定リファレンス
ビルド時に固定される設定プロパティ - それ以外の設定プロパティは実行時に上書き可能
タイプ |
デフォルト |
|
---|---|---|
The realm name Environment variable: |
string |
|
If the properties store is enabled. Environment variable: |
boolean |
|
The sql query to find the password Environment variable: |
string |
|
The data source to use Environment variable: |
string |
|
If the clear-password-mapper is enabled. Environment variable: |
boolean |
|
The index (1 based numbering) of the column containing the clear password Environment variable: |
int |
|
If the bcrypt-password-mapper is enabled. Environment variable: |
boolean |
|
The index (1 based numbering) of the column containing the password hash Environment variable: |
int |
|
A string referencing the password hash encoding ("BASE64" or "HEX") Environment variable: |
|
|
The index (1 based numbering) of the column containing the Bcrypt salt Environment variable: |
int |
|
A string referencing the salt encoding ("BASE64" or "HEX") Environment variable: |
|
|
The index (1 based numbering) of the column containing the Bcrypt iteration count Environment variable: |
int |
|
The index (1 based numbering) of column to map Environment variable: |
int |
|
The target attribute name Environment variable: |
string |
required |
The sql query to find the password Environment variable: |
string |
|
The data source to use Environment variable: |
string |
|
The index (1 based numbering) of column to map Environment variable: |
int |
|
The target attribute name Environment variable: |
string |
required |
If the clear-password-mapper is enabled. Environment variable: |
boolean |
|
The index (1 based numbering) of the column containing the clear password Environment variable: |
int |
|
If the bcrypt-password-mapper is enabled. Environment variable: |
boolean |
|
The index (1 based numbering) of the column containing the password hash Environment variable: |
int |
|
A string referencing the password hash encoding ("BASE64" or "HEX") Environment variable: |
|
|
The index (1 based numbering) of the column containing the Bcrypt salt Environment variable: |
int |
|
A string referencing the salt encoding ("BASE64" or "HEX") Environment variable: |
|
|
The index (1 based numbering) of the column containing the Bcrypt iteration count Environment variable: |
int |
|