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

プロアクティブ認証

設定のカスタマイズや例外処理など、Quarkusでプロアクティブ認証を管理する方法を学びます。 さまざまなアプリケーションシナリオに対する実践的な洞察と戦略を得ることができます。

Quarkusでは、プロアクティブ認証がデフォルトで有効になっています。 これにより、ターゲットページが認証を必要としない場合でも、認証情報を持つすべての受信リクエストが認証されます。 その結果、ターゲットページが公開されている場合でも、無効な認証情報を持つリクエストは拒否されます。

ターゲット・ページが要求したときにのみ認証を行いたい場合は、このデフォルトの動作をオフにすることができます。 プロアクティブ認証をオフにして、ターゲット・ページが要求したときにのみ認証を行なうようにするには、 application.properties の設定ファイルを以下のように変更します:

quarkus.http.auth.proactive=false

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

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

You can still access SecurityIdentity synchronously with public SecurityIdentity getIdentity() in Quarkus REST (formerly RESTEasy Reactive) from endpoints that are annotated with @RolesAllowed, @Authenticated, or with respective configuration authorization checks because authentication has already happened. The same is also valid for Reactive routes if a route response is synchronous.

When proactive authentication is disabled, standard security annotations used on CDI beans do not function on an I/O thread if a secured method that is not void synchronously returns a value. This limitation arises from the necessity for these methods to access SecurityIdentity.

The following example defines HelloResource and HelloService. Any GET request to /hello runs on the I/O thread and throws a BlockingOperationNotAllowedException exception.

例の修正方法は1つではありません:

  • Switch to a worker thread by annotating the hello endpoint with @Blocking.

  • Change the sayHello method return type by using a reactive or asynchronous data type.

  • @RolesAllowed アノテーションをエンドポイントに移動します。 エンドポイントメソッドからの SecurityIdentity へのアクセスは決してブロッキング操作ではないので、これは最も安全な方法のひとつです。

import jakarta.annotation.security.PermitAll;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.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 jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class HelloService {

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

}

認証例外応答のカスタマイズ

Jakarta REST ExceptionMapper を使用して、 io.quarkus.security.AuthenticationFailedException のような Quarkus Security 認証の例外をキャプチャできます。 例:

package io.quarkus.it.keycloak;

import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.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 must handle authentication exceptions themselves to create a correct authentication challenge. For example, io.quarkus.oidc.runtime.CodeAuthenticationMechanism, which manages OpenID Connect (OIDC) authorization code flow authentication, must build a correct redirect URL and set a state cookie. Therefore, avoid using custom exception mappers to customize authentication exceptions thrown by such mechanisms. Instead, a safer approach is to ensure that proactive authentication is enabled and to use Vert.x HTTP route failure handlers. This is because events come to the handler with the correct response status and headers. Then, you must only customize the response; for example:
package io.quarkus.it.keycloak;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.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();
                }
            }
        });
    }
}

関連コンテンツ