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

ビルトイン認証サポート

このドキュメントでは、HTTPベースのFORM認証、BASIC認証、相互TLS認証、およびプロアクティブ認証のためのQuarkusのビルトイン認証メカニズムについて説明します。また、プロアクティブ認証についても説明します。

ベーシック認証

HTTP Basic認証は、Webリソースへのアクセス制御を実施するための最もリソースを必要としない技術の一つです。HTTPヘッダーのフィールドを使用し、HTTPクッキー、セッション識別子、ログインページを必要としません。

WebブラウザのようなHTTPユーザーエージェントは、各HTTPリクエストでユーザー名とパスワードを提供するために、 Authorization ヘッダーを使用します。このヘッダーは、 Authorization: Basic <クレデンシャル> として指定されます。ここで、クレデンシャルは、次の例に示すように、ユーザー ID とパスワードをコロンでつなげた Base64 エンコーディングです。

ユーザー名が Alice でパスワードが secret の場合、HTTP認証ヘッダーは Authorization: Basic QWxjZTpzZWNyZXQ= となり、 QWxjZTpzZWNyZXQ=Alice:secret の文字列をBase64でエンコードしたものです。

Basic 認証の仕組みは、送信されるクレデンシャルに対して機密性の保護を提供しません。クレデンシャルは転送中にBase64でエンコードされるだけで、暗号化もハッシュ化もされません。したがって、Basic 認証は機密性を提供するために HTTPS と共に使用されます。

Basic認証は、すべてのウェブブラウザーとほとんどのウェブサーバーが理解できる、よく規定されたシンプルなチャレンジとレスポンスのスキームです。しかし、ベーシック認証には、以下のような制限があります。

認証情報がプレーンテキストで送信されます

Basic認証で HTTPS を使用すると、クレデンシャルが公開されるのを防ぐことができます。ロードバランサーがHTTPSを終端すると、要求がHTTPでQuarkusに転送されるため、クレデンシャルが平文で公開されるリスクが高まります。

また、マルチホップデプロイメントでは、クライアントと最初のQuarkusエンドポイント間のみでHTTPSを使用し、次のQuarkusエンドポイントにHTTPで認証情報を伝達する場合、認証情報が漏洩する可能性があります。

認証情報はリクエスト毎に送信されます

ベーシック認証では、ユーザー名とパスワードをリクエスト毎に送信する必要があります。これは、認証情報が漏洩するリスクを高めます。

アプリケーションの複雑性の増大

Quarkusアプリケーションは、ユーザー名、パスワード、およびロールが安全に管理されていることを検証する必要があります。しかし、このプロセスは、アプリケーションに大きな複雑さをもたらす可能性があります。ユースケースによっては、ユーザー名、パスワード、ロールの管理を専用のサービスに委ねる他の認証メカニズムの方が良い選択かもしれません。

フォームベース認証

Quarkusには、従来のServletのフォームベース認証と同様に動作する、フォームベース認証があります。従来のフォーム認証とは異なり、Quarkusではクラスタ化されたHTTPセッションがサポートされていないため、認証されたユーザーはHTTPセッションに保存されません。代わりに、認証情報は暗号化されたクッキーに保存されます。このクッキーは、クラスタのすべてのメンバーが読み取ることができます(ただし、すべてのメンバーが同じ暗号化キーを共有している場合)。

暗号化キーは quarkus.http.auth.session.encryption-key プロパティーを使って設定でき、少なくとも 16 文字の長さでなければなりません。このキーは SHA-256 を使ってハッシュ化され、その結果のダイジェストがクッキー値の AES-256 暗号化のキーとして使用されます。このクッキーは暗号化された値の一部として有効期限を含んでいますので、クラスター内のすべての ノードはクロックを同期させなければなりません。1 分間隔で、セッションが使用中であれば、更新された有効期限時間を持つ新しいクッキーが生成されます。

シングルページアプリケーション(SPA)は通常、リダイレクトを避けたいものですが、これはデフォルトページパスを削除することによって実現できます:

# do not redirect, respond with HTTP 200 OK
quarkus.http.auth.form.landing-page=

# do not redirect, respond with HTTP 401 Unauthorized
quarkus.http.auth.form.login-page=
quarkus.http.auth.form.error-page=

以下のプロパティーを使用して、フォームベース認証を設定することができます。

ビルド時に固定される設定プロパティ - それ以外の設定プロパティは実行時に上書き可能

Configuration property

タイプ

デフォルト

If form authentication is enabled.

Environment variable: QUARKUS_HTTP_AUTH_FORM_ENABLED

Show more

boolean

false

The login page. Redirect to login page can be disabled by setting quarkus.http.auth.form.login-page=.

Environment variable: QUARKUS_HTTP_AUTH_FORM_LOGIN_PAGE

Show more

string

/login.html

The post location.

Environment variable: QUARKUS_HTTP_AUTH_FORM_POST_LOCATION

Show more

string

/j_security_check

The username field name.

Environment variable: QUARKUS_HTTP_AUTH_FORM_USERNAME_PARAMETER

Show more

string

j_username

The password field name.

Environment variable: QUARKUS_HTTP_AUTH_FORM_PASSWORD_PARAMETER

Show more

string

j_password

The error page. Redirect to error page can be disabled by setting quarkus.http.auth.form.error-page=.

Environment variable: QUARKUS_HTTP_AUTH_FORM_ERROR_PAGE

Show more

string

/error.html

The landing page to redirect to if there is no saved page to redirect back to. Redirect to landing page can be disabled by setting quarkus.http.auth.form.landing-page=.

Environment variable: QUARKUS_HTTP_AUTH_FORM_LANDING_PAGE

Show more

string

/index.html

Option to control the name of the cookie used to redirect the user back to where he wants to get access to.

Environment variable: QUARKUS_HTTP_AUTH_FORM_LOCATION_COOKIE

Show more

string

quarkus-redirect-location

The inactivity (idle) timeout When inactivity timeout is reached, cookie is not renewed and a new login is enforced.

Environment variable: QUARKUS_HTTP_AUTH_FORM_TIMEOUT

Show more

Duration

PT30M

How old a cookie can get before it will be replaced with a new cookie with an updated timeout, also referred to as "renewal-timeout". Note that smaller values will result in slightly more server load (as new encrypted cookies will be generated more often), however larger values affect the inactivity timeout as the timeout is set when a cookie is generated. For example if this is set to 10 minutes, and the inactivity timeout is 30m, if a users last request is when the cookie is 9m old then the actual timeout will happen 21m after the last request, as the timeout is only refreshed when a new cookie is generated. In other words no timeout is tracked on the server side; the timestamp is encoded and encrypted in the cookie itself, and it is decrypted and parsed with each request.

Environment variable: QUARKUS_HTTP_AUTH_FORM_NEW_COOKIE_INTERVAL

Show more

Duration

PT1M

The cookie that is used to store the persistent session

Environment variable: QUARKUS_HTTP_AUTH_FORM_COOKIE_NAME

Show more

string

quarkus-credential

The cookie path for the session and location cookies.

Environment variable: QUARKUS_HTTP_AUTH_FORM_COOKIE_PATH

Show more

string

/

Set the HttpOnly attribute to prevent access to the cookie via JavaScript.

Environment variable: QUARKUS_HTTP_AUTH_FORM_HTTP_ONLY_COOKIE

Show more

boolean

false

SameSite attribute for the session and location cookies.

Environment variable: QUARKUS_HTTP_AUTH_FORM_COOKIE_SAME_SITE

Show more

strict, lax, none

strict

期間フォーマットについて

期間のフォーマットは標準の java.time.Duration フォーマットを使用します。詳細は Duration#parse() javadoc を参照してください。

数値で始まる期間の値を指定することもできます。この場合、値が数値のみで構成されている場合、コンバーターは値を秒として扱います。そうでない場合は、 PT が暗黙的に値の前に付加され、標準の java.time.Duration 形式が得られます。

相互TLS認証

QuarkusはmTLS認証を提供しているので、X.509証明書に基づいてユーザーを認証できます。

この認証方法を使用するには、まずアプリケーションでSSLを有効にする必要があります。詳しくは、 「Supporting secure connections with SSL」をご覧ください。

アプリケーションが安全な接続を受け入れたら、次のステップは、アプリケーションが信頼すべきすべての証明書を保持する quarkus.http.ssl.certificate.trust-store-file を設定し、クライアント(例: ブラウザや他のサービス)が保護されたリソースにアクセスしようとしたときに、アプリケーションがどのように証明書を要求するかを設定します。

quarkus.http.ssl.certificate.key-store-file=server-keystore.jks            (1)
quarkus.http.ssl.certificate.key-store-password=the_key_store_secret
quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks        (2)
quarkus.http.ssl.certificate.trust-store-password=the_trust_store_secret
quarkus.http.ssl.client-auth=required                                      (3)

quarkus.http.auth.permission.default.paths=/*                              (4)
quarkus.http.auth.permission.default.policy=authenticated
1 サーバーの秘密鍵が置かれている鍵ストアを設定します。
2 信頼された証明書がロードされるトラストストアを構成します。
3 サーバーが *常に*クライアントからの証明書を要求することを定義します。 REQUEST を使用することで、この動作を緩和することができます。mTLS 以外の認証方法もサポートしている場合に便利です。
4 認証されたユーザーのみがアプリケーションからリソースにアクセスできるようにするポリシーを定義します。

受信したリクエストがトラストストアの有効な証明書と一致したら、アプリケーションは以下のように SecurityIdentity を注入するだけでサブジェクトを取得できるようになるはずです。

subjectの取得
@Inject
SecurityIdentity identity;

@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
    return String.format("Hello, %s", identity.getPrincipal().getName());
}

また、以下のように証明書を取得できるようにしておきましょう:

証明書の取得
import java.security.cert.X509Certificate;
import io.quarkus.security.credential.CertificateCredential;

CertificateCredential credential = identity.getCredential(CertificateCredential.class);
X509Certificate certificate = credential.getCertificate();

認可

クライアント証明書の情報は、Quarkus SecurityIdentity を強化するために使用できます。例えば、クライアント証明書のサブジェクト名を確認した後に新しいロールを追加することなどができます。Quarkus SecurityIdentity のカスタマイズの詳細については、 SecurityIdentity Customizationのセクションを参照してください。。

プロアクティブ認証

デフォルトでは、Quarkusはプロアクティブ認証と呼ばれる認証を行います。これは、受信したリクエストにクレデンシャルがある場合、そのリクエストは常に認証されます(ターゲットページが認証を必要としない場合でも)。

これは、公開ページであっても、無効なクレデンシャルを持つリクエストは常に拒否されることを意味します。この動作を変更して、 quarkus.http.auth.proactive=false を設定することで必要な場合のみ認証を行うことができます。

プロアクティブ認証を無効にすると、認証プロセスは ID が要求されたときにのみ実行されます。これは、ユーザーを認証する必要があるセキュリティルールが存在するか、 現在の ID にプログラムによるアクセスがあるためです。

プロアクティブ認証が使用されている場合、 SecurityIdentity へのアクセスはブロックされることに注意してください。これは、認証がまだ行われていない可能性があるためです。また、データベースなどの外部システムを呼び出す必要があるため、ブロックされる可能性があります。ブロッキングアプリケーションでは問題ありませんが、リアクティブアプリケーションで認証を無効にしている場合、これは失敗します(IOスレッドでブロッキング操作を行うことができないため)。これを回避するには、 io.quarkus.security.identity.CurrentIdentityAssociation のインスタンスを @Inject して、 Uni<SecurityIdentity> getDeferredIdentity(); メソッドを呼び出す必要があります。そして、生成された Uni をサブスクライブすることで、認証が完了し、ID が利用可能になったときに通知されるようになります。

@RolesAllowed `, `@Authenticated `, またはそれぞれの設定の認証チェックでアノテーションされたエンドポイントから、 RESTEasy Reactiveの `public SecurityIdentity getIdentity() で同期的に SecurityIdentity にアクセスすることは、すでに認証が行われているため可能です。また、ルート応答が同期的である場合、 Reactiveルートについても 同様です。

CDI Beanの 標準的なセキュリティアノテーション は、 SecurityIdentity にアクセスする必要があるため、非voidセキュアメソッドが同期的に値を返し、プロアクティブ認証が無効な場合、 IOスレッドでサポートされません。 以下の例では、 HelloResourceHelloService を定義しました。 /hello へのGETリクエストは、 IOスレッドで実行され、 BlockingOperationNotAllowedException 例外が投げられることは容易にわかります。この例を修正する方法は1つだけではありません:

  • ワーカスレッドに切り替える ( hello のエンドポイントに @Blocking のアノテーションを付ける)

  • sayHello メソッドの戻り値の型を変更する(リアクティブまたは非同期データ型を使用する)。

  • エンドポイントメソッドから SecurityIdentity へのアクセスはブロック操作にならないため、 @RolesAllowed のアノテーションをエンドポイントに移動させるのが最も安全な方法であることは間違いありません。

import javax.annotation.security.PermitAll;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

import io.smallrye.mutiny.Uni;

@Path("/hello")
@PermitAll
public class HelloResource {

    @Inject
    HelloService helloService;

    @GET
    public Uni<String> hello() {
        return Uni.createFrom().item(helloService.sayHello());
    }

}
import javax.annotation.security.RolesAllowed;
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class HelloService {

    @RolesAllowed("admin")
    public String sayHello() {
        return "Hello";
    }

}

認証の例外応答をカスタマイズする方法

JAX-RS ExceptionMapper を使用すると、例えば io.quarkus.security.AuthenticationFailedException のような Quarkus Security の認証例外を捕捉できます。例:

package io.quarkus.it.keycloak;

import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import io.quarkus.security.AuthenticationFailedException;

@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFailedExceptionMapper implements ExceptionMapper<AuthenticationFailedException> {

    @Context
    UriInfo uriInfo;

    @Override
    public Response toResponse(AuthenticationFailedException exception) {
        return Response.status(401).header("WWW-Authenticate", "Basic realm=\"Quarkus\"").build();
    }
}
HTTP認証メカニズムの中には、正しい認証チャレンジを作成するために、認証例外を自ら処理する必要があるものがあります。例えば、OpenId Connectの認可コードフロー認証を管理する io.quarkus.oidc.runtime.CodeAuthenticationMechanism は、正しいリダイレクトURLやcookieなどを構築する必要があります。そのため、このようなメカニズムが投げる認証例外をカスタム例外マッパーでカスタマイズすることは推奨されません。このような場合、認証例外をカスタマイズする安全な方法は、プロアクティブ認証が無効になっていないことを確認し、イベントが正しい応答ステータスとヘッダーでハンドラに来るように、Vert.x HTTPルート障害ハンドラを使用することです。そのために必要なことは、応答をこのようにカスタマイズすることです:
package io.quarkus.it.keycloak;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;

import io.quarkus.security.AuthenticationFailedException;
import io.vertx.core.Handler;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;

@ApplicationScoped
public class AuthenticationFailedExceptionHandler {

    public void init(@Observes Router router) {
        router.route().failureHandler(new Handler<RoutingContext>() {
            @Override
            public void handle(RoutingContext event) {
                if (event.failure() instanceof AuthenticationFailedException) {
                    event.response().end("CUSTOMIZED_RESPONSE");
                } else {
                    event.next();
                }
            }
        });
    }
}