ビルトイン認証サポート
このドキュメントでは、HTTPベースのFORM認証、BASIC認証、相互TLS認証、およびプロアクティブ認証のためのQuarkusのビルトイン認証メカニズムについて説明します。また、プロアクティブ認証についても説明します。
ベーシック認証
HTTP Basic Authentication is one of the least resource-demanding techniques that enforce access controls to the Web resources. It uses fields in the HTTP header and does not require HTTP cookies, session identifiers, or login pages.
An HTTP user agent, such as a web browser, uses an Authorization
header to provide a user name and password in each HTTP request. The header is specified as Authorization: Basic <credentials>
, where credentials are the Base64 encoding of the user ID and password joined by a colon, as shown in the following example.
If the user name is Alice
and the password is secret
, the HTTP authorization header would be Authorization: Basic QWxjZTpzZWNyZXQ=
, where QWxjZTpzZWNyZXQ=
is a Base64 encoded representation of the Alice:secret
string.
The Basic Authentication mechanism does not provide confidentiality protection for the transmitted credentials. The credentials are merely encoded with Base64 when in transit and not encrypted or hashed in any way. Therefore, Basic Authentication is used with HTTPS to provide confidentiality.
Basic Authentication is a well-specified, simple challenge and response scheme that all web browsers and most web servers understand. However, there are a few limitations associated with Basic Authentication, which include:
- 認証情報がプレーンテキストで送信されます
-
Use HTTPS with Basic Authentication to avoid exposing the credentials. The risk of exposing credentials as plain text increases if a load balancer terminates HTTPS, as the request is forwarded to Quarkus over HTTP.
また、マルチホップデプロイメントでは、クライアントと最初のQuarkusエンドポイント間のみでHTTPSを使用し、次のQuarkusエンドポイントにHTTPで認証情報を伝達する場合、認証情報が漏洩する可能性があります。
- 認証情報はリクエスト毎に送信されます
-
ベーシック認証では、ユーザー名とパスワードをリクエスト毎に送信する必要があります。これは、認証情報が漏洩するリスクを高めます。
- アプリケーションの複雑性の増大
-
The Quarkus application must validate that usernames, passwords, and roles are managed securely. This process, however, can introduce significant complexity to the application. Depending on the use case, other authentication mechanisms that delegate username, password, and role management to specialized services might be a better choice.
フォームベース認証
Quarkusには、従来のServletのフォームベース認証と同様に動作する、フォームベース認証があります。従来のフォーム認証とは異なり、Quarkusではクラスタ化されたHTTPセッションがサポートされていないため、認証されたユーザーはHTTPセッションに保存されません。代わりに、認証情報は暗号化されたクッキーに保存されます。このクッキーは、クラスタのすべてのメンバーが読み取ることができます(ただし、すべてのメンバーが同じ暗号化キーを共有している場合)。
暗号化キーは quarkus.http.auth.session.encryption-key
プロパティーを使って設定でき、少なくとも 16 文字の長さでなければなりません。このキーは SHA-256 を使ってハッシュ化され、その結果のダイジェストがクッキー値の AES-256 暗号化のキーとして使用されます。このクッキーは暗号化された値の一部として有効期限を含んでいますので、クラスター内のすべての ノードはクロックを同期させなければなりません。1 分間隔で、セッションが使用中であれば、更新された有効期限時間を持つ新しいクッキーが生成されます。
Single Page Application (SPA) typically wants to avoid redirects, this can be done by removing default page paths:
# 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=
以下のプロパティーを使用して、フォームベース認証を設定することができます。
ビルド時に固定される設定プロパティ - その他の設定プロパティはランタイムでオーバーライド可能です。
型 |
デフォルト |
|
---|---|---|
If form authentication is enabled. Environment variable: |
boolean |
|
The login page. Redirect to login page can be disabled by setting Environment variable: |
string |
|
The post location. Environment variable: |
string |
|
The username field name. Environment variable: |
string |
|
The password field name. Environment variable: |
string |
|
The error page. Redirect to error page can be disabled by setting Environment variable: |
string |
|
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 Environment variable: |
string |
|
Option to control the name of the cookie used to redirect the user back to where he wants to get access to. Environment variable: |
string |
|
The inactivity (idle) timeout When inactivity timeout is reached, cookie is not renewed and a new login is enforced. Environment variable: |
|
|
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: |
|
|
The cookie that is used to store the persistent session Environment variable: |
string |
|
The cookie path for the session and location cookies. Environment variable: |
string |
|
Set the HttpOnly attribute to prevent access to the cookie via JavaScript. Environment variable: |
boolean |
|
SameSite attribute for the session and location cookies. Environment variable: |
|
|
期間フォーマットについて
期間のフォーマットは標準の 数値で始まる期間の値を指定することもできます。この場合、値が数値のみで構成されている場合、コンバーターは値を秒として扱います。そうでない場合は、 |
相互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
を注入するだけでサブジェクトを取得できるようになるはずです。
@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 が利用可能になったときに通知されるようになります。
It’s still possible to access the SecurityIdentity synchronously with public SecurityIdentity getIdentity() in the RESTEasy Reactive from endpoints annotated with @RolesAllowed , @Authenticated , or with respective configuration authorization checks as authentication has already happened. The same is also valid for the Reactive routes if a route response is synchronous.
|
Standard security annotations on CDI beans are not supported on IO thread if a non-void secured method returns a value synchronously and proactive authentication is disabled, as they need to access the SecurityIdentity
. In the example below, we have defined HelloResource
and HelloService
. It’s easy to see that any GET request to /hello
will run on IO thread and throw BlockingOperationNotAllowedException
exception. There is more than one way to fix the example:
-
switch to a worker thread (annotate
hello
endpoint with@Blocking
) -
change
sayHello
method return type (use reactive or asynchronous data type) -
arguably the safest way is to move
@RolesAllowed
annotation to the endpoint, as accessingSecurityIdentity
from endpoint methods is never the blocking operation
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";
}
}
認証の例外応答をカスタマイズする方法
You can use JAX-RS ExceptionMapper
to capture Quarkus Security authentication exceptions such as io.quarkus.security.AuthenticationFailedException
, for example:
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();
}
}
Some HTTP authentication mechanisms need to handle authentication exceptions themselves in order to create a correct authentication challenge. For example, io.quarkus.oidc.runtime.CodeAuthenticationMechanism which manages OpenId Connect authorization code flow authentication, needs to build a correct redirect URL, cookies, etc. For that reason, using custom exception mappers to customize authentication exceptions thrown by such mechanisms is not recommended. In such cases, a safer way to customize authentication exceptions is to make sure the proactive authentication is not disabled and use Vert.x HTTP route failure handlers, as events come to the handler with the correct response status and headers. To that end, the only thing that needs to be done is to customize the response like this:
|
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();
}
}
});
}
}