The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
このページを編集

ウェブエンドポイントの認可

Quarkus には、プラグ可能な Web セキュリティーレイヤーが組み込まれています。セキュリティーがアクティブな場合、システムはすべての HTTP リクエストに対して権限チェックを実行し、続行するかどうかを決定します。

認可は、セキュリティープロバイダーが提供するユーザーロールに基づいています。これらのロールをカスタマイズするには、 SecurityIdentityAugmentor を作成できます。 Security Identity Customization を参照してください。

設定を利用した認可

If you work with Jakarta RESTful Web Services (JAX-RS) and need to set default security requirements, consider using アノテーションを使用した認可 and quarkus.security.jaxrs.deny-unannotated-endpoints or quarkus.security.jaxrs.default-roles-allowed properties instead of the HTTP security policy path-level matching because the security annotations can override these properties on an individual JAX-RS resource or method level.

権限は、Quarkus 設定で権限セットによって定義され、それぞれがアクセス制御のポリシーを指定します。

セキュリティーポリシーの paths プロパティーに現在のリクエストパスとマッチする最も具体的なパスが含まれている場合、それはマッチするパスを持つ他のセキュリティーポリシーよりも優先され、勝利すると言われます。

Configured HTTP security policy must not contain a semicolon ';' character in its paths property. Use カスタム HttpSecurityPolicy when a security policy decision depends on a presence of certain matrix parameters in the request path.

Be careful with creating complex, possibly overlapping HTTP security policy path expressions. Make sure your HTTP policy configuration is thoroughly tested. If you work with Jakarta RESTful Web Services (JAX-RS) and need to create complex security policies, consider using アノテーションを使用した認可 instead.

Table 1. Quarkus ポリシーの概要
組み込みポリシー 説明

deny

このポリシーは、すべてのユーザーを拒否します。

permit

このポリシーは、すべてのユーザーを許可します。

authenticated

このポリシーは、認証されたユーザーのみを許可します。

特定のロールを持つユーザーにリソースへのアクセスを許可するロールベースのポリシーを定義できます。

ロールベースポリシーの例
quarkus.http.auth.policy.role-policy1.roles-allowed=user,admin                  (1)
1 これは、 user ロールと admin ロールを持つユーザーを許可するロールベースのポリシーを定義します。

次の設定例に示すように、 application.properties ファイルで定義されている組み込みの権限セットを設定することで、カスタムポリシーを参照できます。

ポリシーの設定例
quarkus.http.auth.permission.permit1.paths=/public/*                            (1)
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET

quarkus.http.auth.permission.deny1.paths=/forbidden                             (2)
quarkus.http.auth.permission.deny1.policy=deny

quarkus.http.auth.permission.roles1.paths=/roles-secured/*,/other/*,/api/*      (3)
quarkus.http.auth.permission.roles1.policy=role-policy1
1 この権限は、デフォルトの組み込み permit ポリシーを参照し、 /public への GET メソッドを許可します。この場合、このリクエストは常に許可されるため、示されている設定はこの例には影響しません。
2 この権限は、 /forbidden パスと /forbidden/ パスの両方に対して組み込みの deny ポリシーを参照します。これは * で終わっていないため、正確なパスマッチとなります。
3 この権限セットは、以前に定義されたポリシーを参照します。 roles1 は例の名前です。権限セットには任意の名前を付けることができます。

上記の例の正確なパスパターン /forbidden は、 /forbidden/ パスも保護します。このようにして、以下の例の forbidden エンドポイントは deny1 権限によって保護されます。

package org.acme.crud;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/forbidden")
public class ForbiddenResource {
    @GET
    public String forbidden() { (1)
        return "No!";
    }
}
1 forbidden エンドポイントを保護するには、 /forbidden パスと /forbidden/ パスの両方を保護する必要があります。

/forbidden/ パスへのアクセスを許可する必要がある場合は、以下の例のように、より具体的な正確なパスを使用して新しい権限を追加してください。

quarkus.http.auth.permission.permit1.paths=/forbidden/ (1)
quarkus.http.auth.permission.permit1.policy=permit
1 /forbidden/ パスは保護されていません。

カスタム HttpSecurityPolicy

独自の名前付きポリシーを登録すると便利な場合があります。以下の例のように、 io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy インターフェイスを実装するアプリケーションスコープの CDI Bean を作成することでこれを実現できます。

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

@ApplicationScoped
public class CustomNamedHttpSecPolicy implements HttpSecurityPolicy {
    @Override
    public Uni<CheckResult> checkPermission(RoutingContext event, Uni<SecurityIdentity> identity,
            AuthorizationRequestContext requestContext) {
        if (customRequestAuthorization(event)) {
            return CheckResult.permit();
        }
        return CheckResult.deny();
    }

    @Override
    public String name() {
        return "custom"; (1)
    }

    private static boolean customRequestAuthorization(RoutingContext event) {
        // here comes your own security check
        return !event.request().path().endsWith("denied");
    }
}
1 名前付き HTTP セキュリティーポリシーは、 application.properties パスマッチングルールにマッチするリクエストにのみ適用されます。
設定ファイルから参照されるカスタム名の HttpSecurityPolicy の例
quarkus.http.auth.permission.custom1.paths=/custom/*
quarkus.http.auth.permission.custom1.policy=custom                              (1)
1 カスタムポリシー名は、 io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy.name メソッドによって返される値とマッチする必要があります。

または、 @AuthorizationPolicy セキュリティーアノテーションを使用して、カスタム名の HttpSecurityPolicy を Jakarta REST エンドポイントにバインドすることもできます。

Jakarta REST エンドポイントにバインドされたカスタム名の HttpSecurityPolicy の例
import io.quarkus.vertx.http.security.AuthorizationPolicy;
import jakarta.annotation.security.DenyAll;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@DenyAll (1)
@Path("hello")
public class HelloResource {

    @AuthorizationPolicy(name = "custom") (2)
    @GET
    public String hello() {
        return "hello";
    }

}
1 @AuthorizationPolicy アノテーションは、他の標準セキュリティーアノテーションと合わせて使用できます。通常どおり、メソッドレベルのアノテーションはクラスレベルのアノテーションよりも優先されます。
2 カスタム名の HttpSecurityPolicy を Jakarta REST hello エンドポイントに適用します。

すべてのリクエストで呼び出されるグローバル HttpSecurityPolicy を作成することもできます。io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy.name メソッドを実装せず、ポリシーに名前を付けないままにします。

@RequestScoped Bean の HttpSecurityPolicy への挿入

@RequestScoped Bean は、 CDI リクエストコンテキスト がアクティブな場合にのみ注入できます。コンテキストは、例えば @ActivateRequestContext を使用してユーザーによってアクティブ化できますが、認可は Quarkus が一部の @RequestScoped Bean を準備する前に行われます。Quarkus が CDI リクエストコンテキストをアクティブ化して準備することをお勧めします。たとえば、 jakarta.ws.rs.core.UriInfo Bean のような Jakarta REST コンテキストから Bean を注入したい状況を考えてみましょう。この場合、Jakarta REST エンドポイントに HttpSecurityPolicy を適用する必要があります。これは、次のいずれかの方法で実現できます。

  • @AuthorizationPolicy セキュリティーアノテーションを使用します。

  • quarkus.http.auth.permission.custom1.applies-to=jaxrs 設定プロパティーを設定します。

パスとメソッドのマッチング

権限セットでは、パスとメソッドをコンマ区切りのリストとして指定することもできます。パスが * ワイルドカードで終わる場合、生成されるクエリーはすべてのサブパスにマッチします。それ以外の場合は、完全一致を照会し、その特定のパスにのみマッチします。

quarkus.http.auth.permission.permit1.paths=/public*,/css/*,/js/*,/robots.txt    (1)
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD
1 パスの末尾にある * ワイルドカードは、0 個以上のパスセグメントにマッチしますが、 /public パスから始まる単語にはマッチしません。そのため、 /public-info のようなパスはこのパターンとはマッチしません。

パスはマッチするがメソッドはマッチしない

パスに基づいて 1 つ以上の権限セットにマッチしても、必要なメソッドのいずれにもマッチしない場合、リクエストは拒否されます。

上記の権限セットが与えられている場合、 GET /public/foo はパスとメソッドの両方にマッチするため、許可されます。対照的に、 POST /public/foo はパスにはマッチしますがメソッドにはマッチしないため、拒否されます。

複数のパスのマッチング:最長パスが優先されます

マッチングは常に「最長パスが優先される」という基準で行われます。より具体的な権限セットがマッチした場合、それよりも具体的な権限セットは考慮されません。

quarkus.http.auth.permission.permit1.paths=/public/*
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD

quarkus.http.auth.permission.deny1.paths=/public/forbidden-folder/*
quarkus.http.auth.permission.deny1.policy=deny

上記の権限セットの場合、 GET /public/forbidden-folder/foo は両方の権限セットのパスにマッチします。しかし、より長いパスが deny1 権限セットのパスにマッチするため、 deny1 が選択され、リクエストは拒否されます。

deny1permit1 の権限に関する以前の例で示したように、サブパスの権限はルートパスの権限よりも優先されます。

このルールは、サブパス権限が公開リソースへのアクセスを許可する一方で、ルートパス権限が認可を必要とするシナリオによってさらに例示されます。

quarkus.http.auth.policy.user-policy.roles-allowed=user
quarkus.http.auth.permission.roles.paths=/api/*
quarkus.http.auth.permission.roles.policy=user-policy

quarkus.http.auth.permission.public.paths=/api/noauth/*
quarkus.http.auth.permission.public.policy=permit

複数のサブパスのマッチング: * ワイルドカードへの最長パスが優先されます

以前の例では、パスが * ワイルドカードで終わる場合に、すべてのサブパスがマッチすることを示しました。

このワイルドカードはパスの途中にも適用でき、単一のパスセグメントを表します。他のパスセグメント文字と混在させることはできません。したがって、 /public/*/about-us のパスに見られるように、パス区切り文字は常に * ワイルドカードを囲みます。

複数のパスパターンが同じリクエストパスに対応する場合、システムは * ワイルドカードにつながる最長のサブパスを選択します。この文脈では、すべてのパスセグメント文字は * ワイルドカードよりも具体的です。

簡単な例を次に示します。

quarkus.http.auth.permission.secured.paths=/api/*/detail                    (1)
quarkus.http.auth.permission.secured.policy=authenticated
quarkus.http.auth.permission.public.paths=/api/public-product/detail        (2)
quarkus.http.auth.permission.public.policy=permit
1 /api/product/detail のようなリクエストパスには、認証済みユーザーのみがアクセスできます。
2 パス /api/public-product/detail はより具体的であるため、誰でもアクセスできます。

設定を使用した認可で保護されたすべてのパスをテストする必要があります。複数のワイルドカードを使用してパスパターンを記述するのは面倒な場合があります。パスが意図したとおりに認可されていることを確認してください。

次の例では、パスは最も具体的なものから最も具体的でないものの順に並んでいます。

リクエストパス /one/two/three/four/five は、最も具体的なパスから最も具体的でないパスの順にマッチします
/one/two/three/four/five
/one/two/three/four/*
/one/two/three/*/five
/one/two/three/*/*
/one/two/*/four/five
/one/*/three/four/five
/*/two/three/four/five
/*/two/three/*/five
/*

パスの末尾にある * ワイルドカードは、0 個以上のパスセグメントにマッチします。その他の場所に配置された * ワイルドカードは、正確に 1 つのパスセグメントにマッチします。

複数のパスのマッチング: 最も具体的なメソッドが優先されます

パスが複数の権限セットに登録されている場合、リクエストにマッチする HTTP メソッドを明示的に指定している権限セットが優先されます。この場合、メソッドのない権限セットは、リクエストメソッドがメソッド仕様を持つ権限セットとマッチしない場合にのみ有効になります。

quarkus.http.auth.permission.permit1.paths=/public/*
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD

quarkus.http.auth.permission.deny1.paths=/public/*
quarkus.http.auth.permission.deny1.policy=deny

上記の権限セットは、 GET /public/foo が両方の権限セットのパスにマッチすることを示しています。ただし、これは permit1 権限セットの明示的なメソッドと明確に一致しています。したがって、 permit1 が選択され、リクエストは受け入れられます。

対照的に、 PUT /public/foopermit1 のメソッド権限にマッチしません。その結果、 deny1 がアクティブになり、リクエストの拒否につながります。

複数のパスとメソッドのマッチング: 両方が優先されます

場合によっては、前述のルールにより、複数の権限セットが同時に適用されることがあります。その場合、リクエストを続行するには、すべての権限でアクセスが許可されている必要があります。そのためには、両方にメソッドが指定されているか、メソッドが存在しない必要があります。メソッド固有のマッチが優先されます。

quarkus.http.auth.policy.user-policy1.roles-allowed=user
quarkus.http.auth.policy.admin-policy1.roles-allowed=admin

quarkus.http.auth.permission.roles1.paths=/api/*,/restricted/*
quarkus.http.auth.permission.roles1.policy=user-policy1

quarkus.http.auth.permission.roles2.paths=/api/*,/admin/*
quarkus.http.auth.permission.roles2.policy=admin-policy1
上記の権限セットの場合、 GET /api/foo は両方の権限セットのパスにマッチするため、 useradmin の両方のロールが必要です。

アクセスを拒否するための設定プロパティー

以下の設定により、ロールベースアクセスコントロール (RBAC) の拒否動作が変更されます:

quarkus.security.jaxrs.deny-unannotated-endpoints=true|false

true に設定すると、すべての Jakarta REST エンドポイントへのアクセスがデフォルトで拒否されます。Jakarta REST エンドポイントにセキュリティーアノテーションがない場合、デフォルトで @DenyAll 動作になります。これにより、保護されているはずのエンドポイントが誤って公開されることを回避できます。デフォルトは false です。

quarkus.security.jaxrs.default-roles-allowed=role1,role2

アノテーションのないエンドポイントのデフォルトのロール要件を定義します。 ** ロールは、認証されたすべてのユーザーを意味する特別なロールです。代わりに deny が有効になるため、これを deny-unannotated-endpoints と組み合わせることはできません。

quarkus.security.deny-unannotated-members=true|false

true に設定すると、セキュリティーアノテーションを持たないが、セキュリティーアノテーションを持つメソッドを含むクラスで定義されているすべての CDI メソッドと Jakarta REST エンドポイントへのアクセスが拒否されます。デフォルトは false です。

権限の無効化

権限は、宣言された各権限の enabled プロパティを使って、ビルド時に次のように無効にすることができます:

quarkus.http.auth.permission.permit1.enabled=false
quarkus.http.auth.permission.permit1.paths=/public/*,/css/*,/js/*,/robots.txt
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD

権限は、システムプロパティーや環境変数を使って実行時に再有効化することができます(例: -Dquarkus.http.auth.permission.permit1.enabled=true)。

権限パスと HTTP ルートパス

quarkus.http.root-path 設定プロパティーは、 HTTP エンドポイントコンテキストパス を変更します。

デフォルトでは、 quarkus.http.root-path は設定された権限パスの前に自動的に追加され、フォワードスラッシュを使用しません。例えば:

quarkus.http.auth.permission.permit1.paths=public/*,css/*,js/*,robots.txt

この設定は以下と同等です:

quarkus.http.auth.permission.permit1.paths=${quarkus.http.root-path}/public/*,${quarkus.http.root-path}/css/*,${quarkus.http.root-path}/js/*,${quarkus.http.root-path}/robots.txt

先頭のスラッシュは、設定された権限パスの解釈方法を変更します。設定された URL はそのまま使用され、 quarkus.http.root-path の値が変更されてもパスは調整されません。

例:
quarkus.http.auth.permission.permit1.paths=/public/*,css/*,js/*,robots.txt

この設定は、固定または静的 URL /public から提供されるリソースにのみ影響します。 quarkus.http.root-path/ 以外に設定されている場合、この URL はアプリケーションリソースとマッチしない可能性があります。

詳細については、Path Resolution in Quarkus を参照してください。

SecurityIdentity ロールのマッピング

現在のリクエストを認可するために選択された優先ロールベースポリシーは、 SecurityIdentity ロールをデプロイメント固有のロールにマッピングできます。これらのロールは、 @RolesAllowed アノテーションを使用してエンドポイント認可に適用できます。

quarkus.http.auth.policy.admin-policy1.roles.admin=Admin1 (1)
quarkus.http.auth.permission.roles1.paths=/* (2)
quarkus.http.auth.permission.roles1.policy=admin-policy1
1 admin ロールを Admin1 ロールにマッピングします。 SecurityIdentity には adminAdmin1 の両方のロールが含まれます。
2 /* パスは保護されており、認証された HTTP リクエストのみにアクセスが許可されます。

パスに関係なく、 SecurityIdentity ロールをデプロイメント固有のロールにマッピングするだけでよい場合は、次のようにすることもできます:

quarkus.http.auth.roles-mapping.admin=Admin1 (1) (2)
1 admin ロールを Admin1 ロールにマッピングします。 SecurityIdentity には adminAdmin1 の両方のロールが含まれます。
2 /* パスは保護されていません。標準のセキュリティーアノテーションを使用してエンドポイントを保護するか、この設定プロパティーに加えて HTTP 権限を定義する必要があります。

プログラムによる設定を希望する場合は、同じマッピングを io.quarkus.vertx.http.security.HttpSecurity CDI イベントで追加できます:

package org.acme.http.security;

import io.quarkus.vertx.http.security.HttpSecurity;
import jakarta.enterprise.event.Observes;

public class HttpSecurityConfiguration {

    void configure(@Observes HttpSecurity httpSecurity) {
        httpSecurity.rolesMapping("admin", "Admin1");
    }
}

共有権限チェック

非共有権限チェックの重要なルールとして、最も具体的な 1 つのパスマッチのみが適用されます。パスが最も具体的なものとしてマッチする場合、そのパスに対して複数の権限を指定でき、それらはすべて適用されます。しかし、何度も繰り返さずに多くのパスに適用したい権限チェックがあるかもしれません。そこで共有権限チェックが役立ちます。共有権限チェックは、権限パスがマッチしたときに常に適用されます。

すべての HTTP リクエストに適用されるカスタム名前付き HttpSecurityPolicy の例
quarkus.http.auth.permission.custom1.paths=/*
quarkus.http.auth.permission.custom1.shared=true    (1)
quarkus.http.auth.permission.custom1.policy=custom

quarkus.http.auth.policy.admin-policy1.roles-allowed=admin
quarkus.http.auth.permission.roles1.paths=/admin/*
quarkus.http.auth.permission.roles1.policy=admin-policy1
1 カスタム HttpSecurityPolicy は、 admin-policy1 ポリシーとともに /admin/1 パスにも適用されます。
多数の共有権限チェックを設定することは、非共有の権限チェックを設定するよりも効果が低くなります。以下の例のように、共有権限を使用して非共有権限チェックを補完してください。
共有権限を持つ SecurityIdentity ロールをマッピングします
quarkus.http.auth.policy.role-policy1.roles.root=admin,user (1)
quarkus.http.auth.permission.roles1.paths=/secured/*        (2)
quarkus.http.auth.permission.roles1.policy=role-policy1
quarkus.http.auth.permission.roles1.shared=true

quarkus.http.auth.policy.role-policy2.roles-allowed=user    (3)
quarkus.http.auth.permission.roles2.paths=/secured/user/*
quarkus.http.auth.permission.roles2.policy=role-policy2

quarkus.http.auth.policy.role-policy3.roles-allowed=admin
quarkus.http.auth.permission.roles3.paths=/secured/admin/*
quarkus.http.auth.permission.roles3.policy=role-policy3
1 ロール root は、 /secured/user/* および /secured/admin/* パスにアクセスできます。
2 /secured/* パスには、認証されたユーザーのみがアクセスできます。このようにして、 /secured/all パスなどが保護されます。
3 共有権限は常に非共有権限より前に適用されるため、 root ロールを持つ SecurityIdentity には user ロールも付与されます。

パス固有の認可をプログラムで設定します

これまでにこのガイドで紹介した認可ポリシーは、プログラムで設定することもできます。先ほど述べた例を考えてみましょう。

quarkus.http.auth.permission.permit1.paths=/public/*
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET

quarkus.http.auth.permission.deny1.paths=/forbidden
quarkus.http.auth.permission.deny1.policy=deny

quarkus.http.auth.permission.roles1.paths=/roles-secured/*,/other/*,/api/*
quarkus.http.auth.permission.roles1.policy=role-policy1
quarkus.http.auth.policy.role-policy1.roles-allowed=user,admin

同じ認可ポリシーをプログラムで設定できます。

package org.acme.http.security;

import jakarta.enterprise.event.Observes;

import io.quarkus.vertx.http.security.HttpSecurity;

public class HttpSecurityConfiguration {

    void configure(@Observes HttpSecurity httpSecurity) {
        httpSecurity
                .get("/public/*").permit()
                .path("/roles-secured/*", "/other/*", "/api/*").roles("admin", "user")
                .path("/forbidden").authorization().deny();
    }

}

さらに、 io.quarkus.vertx.http.security.HttpSecurity CDI イベントを使用して、特定の認証メカニズムとポリシーを設定できます。

package org.acme.http.security;

import io.quarkus.vertx.http.security.HttpSecurity;
import jakarta.enterprise.event.Observes;
import org.eclipse.microprofile.config.inject.ConfigProperty;

public class HttpSecurityConfiguration {

    void configure(@Observes HttpSecurity httpSecurity, CustomHttpSecurityPolicy customHttpSecurityPolicy,
                        @ConfigProperty(name = "secured-path") String securedPath) {

        httpSecurity.path("/other/*").basic().policy(customHttpSecurityPolicy); (1)

        httpSecurity.path("/roles-secured/*").bearer().authorization()
                .policy(identity -> identity.hasRole("user") || "root".equals(identity.getPrincipal().getName()));  (2)

        httpSecurity.path("/other/administration").authorizationCodeFlow()
                .authorization().policy((identity, routingContext) -> {
                    if (!identity.isAnonymous()) {
                        String customAuthorization = routingContext.request().getHeader("Custom Authorization");
                        return yourCustomAuthorizationCheck(customAuthorization);
                    }
                    return false;
                });     (3)

        httpSecurity.path(securedPath).form();  (4)

        httpSecurity.path("/user-info").bearer().authorization().permissions("openid", "email", "profile"); (5)
    }
}
1 Basic 認証を使用し、カスタムの io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy でリクエストを認可します。
2 ベアラー トークン認証を使用し、独自のポリシーで SecurityIdentity を認可します。
3 認可コードフローメカニズムを使用し、受信リクエストヘッダーに基づいて独自のポリシーを作成します。
4 Quarkus が HttpSecurity CDI イベントを発生させると、ランタイム設定の準備が整います。
5 /user-info パスへのすべてのリクエストに、文字列権限 openidemailprofile があることを要求します。同じ認可は、エンドポイントに配置された @PermissionsAllowed(value = { "openid", "email", "profile" }, inclusive = true) アノテーションインスタンスで要求できます。

アノテーションを使用した認可

Quarkus には、REST エンドポイントおよび CDI Bean の一般的なセキュリティーアノテーション @RolesAllowed@DenyAll@PermitAll に基づく ロールベースアクセス制御 (RBAC) を可能にする組み込みのセキュリティーが含まれています。

quarkus.http.auth. 設定の認可チェックは、標準セキュリティーアノテーションのセキュリティーチェックの前に実行されます。そのため、 @PermitAll は、HTTP 権限によってまだ制限されていないパスへのアクセスのみを許可します。 @PermitAll は HTTP レベルのセキュリティー設定をオーバーライドすることはできません。 @RolesAllowed などの他の標準セキュリティーアノテーションによって課せられた制限を緩和するだけです。

Table 2. Quarkus アノテーションの種類の概要
アノテーション型 説明

@DenyAll

どのセキュリティーロールも指定されたメソッドを呼び出すことを許可されていないことを指定します。

@PermitAll

すべてのセキュリティーロールが指定されたメソッドを呼び出すことを許可されるように指定します。 @PermitAll は、認証なしでも誰でもアクセスできるようにします。

@RolesAllowed

アプリケーション内のメソッドへのアクセスを許可するセキュリティーロールのリストを指定します。

@Authenticated

Quarkus は、認証されたすべてのユーザーがリソースにアクセスできるようにする io.quarkus.security.Authenticated アノテーションを提供します。これは @RolesAllowed("**") と同等です。

@PermissionsAllowed

指定されたメソッドを呼び出すことが許可される権限のリストを指定します。

@AuthorizationPolicy

指定された Jakarta REST エンドポイントへのアクセスを認可する名前付き io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy を指定します。名前付き HttpSecurityPolicy は、Jakarta REST エンドポイントにバインドされたカスタム名の HttpSecurityPolicy の例 で示されているように、一般的な認可チェックに使用できます。

次の SubjectExposingResource の例 は、Jakarta REST と共通セキュリティーアノテーションの両方を使用してエンドポイントを記述および保護するエンドポイントを示します。

SubjectExposingResource の例
import java.security.Principal;

import jakarta.annotation.security.DenyAll;
import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.SecurityContext;

@Path("subject")
public class SubjectExposingResource {

    @GET
    @Path("secured")
    @RolesAllowed("Tester") (1)
    public String getSubjectSecured(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal(); (2)
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }

    @GET
    @Path("authenticated")
    @Authenticated (3)
    public String getSubjectAuthenticated(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal();
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }

    @GET
    @Path("unsecured")
    @PermitAll (4)
    public String getSubjectUnsecured(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal(); (5)
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }

    @GET
    @Path("denied")
    @DenyAll (6)
    public String getSubjectDenied(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal();
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }
}
1 /subject/secured エンドポイントは、 @RolesAllowed("Tester") アノテーションを使用して "Tester" ロールを付与された認証済みユーザーが必要です。
2 エンドポイントは、Jakarta REST SecurityContext からユーザープリンシパルを取得します。これは、保護されたエンドポイントに対して non-null を返します。
3 /subject/authenticated エンドポイントは、 @Authenticated アノテーションを指定することで、認証されたすべてのユーザーを許可します。
4 /subject/unsecured エンドポイントは、 @PermitAll アノテーションを指定することで、認証されていないアクセスが可能になります。
5 ユーザープリンシパルを取得するための呼び出しは、呼び出し元が認証されていない場合は null を返し、呼び出し元が認証されている場合は non-null を返します。
6 /subject/denied エンドポイントは @DenyAll アノテーションを宣言し、ユーザーが呼び出すかどうかにかかわらず、REST メソッドとしてのすべての直接アクセスを禁止します。このメソッドは、このクラスの他のメソッドによって引き続き内部的に呼び出すことができます。

IO スレッドで標準のセキュリティーアノテーションを使用する予定の場合は、プロアクティブ認証 の情報を確認してください。

アノテーション値 @RolesAllowed は、デフォルト値とネストされたプロパティー式を含む プロパティー式 をサポートします。アノテーションで使用される設定プロパティーは実行時に解決されます。

Table 3. アノテーション値の例
アノテーション 値の説明

@RolesAllowed("${admin-role}")

エンドポイントは、 admin-role プロパティーの値によって示されるロールを持つユーザーを許可します。

@RolesAllowed("${tester.group}-${tester.role}")

値が複数の変数を含むことができることを示す例。

@RolesAllowed("${customer:User}")

デフォルト値のデモンストレーション。 必要なロールは、 customer プロパティーの値によって示されます。 ただし、そのプロパティーが指定されていない場合は、デフォルトで User という名前のロールが必要になります。

@RolesAllowed アノテーションにおけるプロパティ式の使用例
admin=Administrator
tester.group=Software
tester.role=Tester
%prod.secured=User
%dev.secured=**
all-roles=Administrator,Software,Tester,User
サブジェクトアクセス制御の例
import java.security.Principal;

import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.SecurityContext;

@Path("subject")
public class SubjectExposingResource {

    @GET
    @Path("admin")
    @RolesAllowed("${admin}") (1)
    public String getSubjectSecuredAdmin(@Context SecurityContext sec) {
        return getUsername(sec);
    }

    @GET
    @Path("software-tester")
    @RolesAllowed("${tester.group}-${tester.role}") (2)
    public String getSubjectSoftwareTester(@Context SecurityContext sec) {
        return getUsername(sec);
    }

    @GET
    @Path("user")
    @RolesAllowed("${customer:User}") (3)
    public String getSubjectUser(@Context SecurityContext sec) {
        return getUsername(sec);
    }

    @GET
    @Path("secured")
    @RolesAllowed("${secured}") (4)
    public String getSubjectSecured(@Context SecurityContext sec) {
        return getUsername(sec);
    }

    @GET
    @Path("list")
    @RolesAllowed("${all-roles}") (5)
    public String getSubjectList(@Context SecurityContext sec) {
        return getUsername(sec);
    }

    private String getUsername(SecurityContext sec) {
        Principal user = sec.getUserPrincipal();
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }
}
1 @RolesAllowed アノテーション値は、 Administrator の値に設定されています。
2 この /subject/software-tester エンドポイントには、"Software-Tester" のロールが付与された認証済みユーザーが必要です。 ロール定義では複数の式を使用できます。
3 この /subject/user エンドポイントは、customer 設定プロパティーを設定しなかったため、@RolesAllowed("${customer:User}") アノテーションを使用して "User" ロールが付与された認証済みユーザーを必要とします。
4 実稼働環境では、この /subject/secured エンドポイントには User ロールを持つ認証済みユーザーが必要です。 開発モードでは、認証されたすべてのユーザーが許可されます。
5 プロパティー式 all-roles は、コレクション型 List として扱われます。そのため、このエンドポイントには、 AdministratorSoftwareTester および User の各ロールがアクセスできるようになります。

エンドポイントセキュリティーアノテーションと Jakarta REST 継承

Quarkus は、次の例のように、エンドポイント実装またはそのクラスに配置されたセキュリティーアノテーションをサポートしています。

@Path("hello")
public interface HelloInterface {

    @GET
    String hello();

}

@DenyAll (1)
public class HelloInterfaceImpl implements HelloInterface {

    @RolesAllowed("admin") (2)
    @Override
    public String hello() {
        return "Hello";
    }
}
1 クラスレベルのセキュリティーアノテーションは、エンドポイント実装が宣言されているクラスに配置する必要があります。
2 メソッドレベルのセキュリティーアノテーションはエンドポイント実装に配置する必要があります。

デフォルトのインターフェイスメソッドとして宣言された RESTEasy サブリソースロケーターは 標準のセキュリティーアノテーションでは保護できません。 保護されたサブリソースロケーターは、次の例のように、インターフェイス実装者に実装され、保護される必要があります。

@Path("hello")
public interface HelloInterface {

    @RolesAllowed("admin")
    @Path("sub")
    default HelloSubResource wrongWay() {
        // not supported
    }

    @Path("sub")
    HelloSubResource rightWay();

}

public class HelloInterfaceImpl implements HelloInterface {

    @RolesAllowed("admin")
    @Override
    public HelloSubResource rightWay() {
        return new HelloSubResource();
    }
}

権限アノテーション

Quarkus は、指定された権限を持つ認証済みユーザーにリソースへのアクセスを認可する io.quarkus.security.PermissionsAllowed アノテーションも提供します。 このアノテーションは、一般的なセキュリティーアノテーションのエクステンションであり、 SecurityIdentity インスタンスに付与された権限をチェックします。

@PermissionsAllowed アノテーションで保護されたエンドポイントの例
package org.acme.crud;

import io.quarkus.arc.Arc;
import io.vertx.ext.web.RoutingContext;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;

import io.quarkus.security.PermissionsAllowed;

import java.security.BasicPermission;
import java.security.Permission;
import java.util.Collection;
import java.util.Collections;

@Path("/crud")
public class CRUDResource {

    @PermissionsAllowed("create") (1)
    @PermissionsAllowed("update")
    @POST
    @Path("/modify/repeated")
    public String createOrUpdate() {
        return "modified";
    }

    @PermissionsAllowed(value = {"create", "update"}, inclusive=true) (2)
    @POST
    @Path("/modify/inclusive")
    public String createOrUpdate(Long id) {
        return id + " modified";
    }

    @PermissionsAllowed({"see:detail", "see:all", "read"}) (3)
    @GET
    @Path("/id/{id}")
    public String getItem(String id) {
        return "item-detail-" + id;
    }

    @PermissionsAllowed(value = "list", permission = CustomPermission.class) (4)
    @Path("/list")
    @GET
    public Collection<String> list(@QueryParam("query-options") String queryOptions) {
        // your business logic comes here
        return Collections.emptySet();
    }

    public static class CustomPermission extends BasicPermission {

        public CustomPermission(String name) {
            super(name);
        }

        @Override
        public boolean implies(Permission permission) {
            var event = Arc.container().instance(RoutingContext.class).get(); (5)
            var publicContent = "public-content".equals(event.request().params().get("query-options"));
            var hasPermission = getName().equals(permission.getName());
            return hasPermission && publicContent;
        }
    }
}
1 リソースメソッド createOrUpdate にアクセスできるのは、 create 権限と update 権限の両方を持つユーザーのみです。
2 デフォルトでは、1 つのアノテーションインスタンスを通じて指定された権限の少なくとも 1 つが必要です。 inclusive=true を設定することで、すべての権限を要求することができます。 両方のリソースメソッド createOrUpdate には同等の認可要件があります。
3 SecurityIdentityread 権限または see 権限と、 all アクションまたは detail アクションのどちらかがある場合は、 getItem へのアクセスが許可されます。
4 希望する java.security.Permission 実装を使用できます。 デフォルトでは、文字列ベースの権限は io.quarkus.security.StringPermission によって実行されます。
5 権限は Bean ではないため、Bean インスタンスを取得する唯一の方法は、 Arc.container() を使用してプログラム的に取得することです。
IO スレッドで @PermissionsAllowed を使用する予定の場合は プロアクティブ認証 の情報を確認してください。
Quarkus インターセプターの制限により、 @PermissionsAllowed はクラスレベルで繰り返すことができません。 詳細は、Quarkus CDI リファレンスガイドの Repeatable interceptor bindings セクションを参照してください。

ロール対応の SecurityIdentity インスタンスに権限を追加する最も簡単な方法は、ロールを権限にマップすることです。 設定を利用した認可 を使用して、次の例に示すように、認証されたリクエストに対して CRUDResource エンドポイントに必要な SecurityIdentity 権限を付与します。

quarkus.http.auth.policy.role-policy1.permissions.user=see:all                                      (1)
quarkus.http.auth.policy.role-policy1.permissions.admin=create,update,read                          (2)
quarkus.http.auth.permission.roles1.paths=/crud/modify/*,/crud/id/*                                 (3)
quarkus.http.auth.permission.roles1.policy=role-policy1

quarkus.http.auth.policy.role-policy2.permissions.user=list
quarkus.http.auth.policy.role-policy2.permission-class=org.acme.crud.CRUDResource$CustomPermission  (4)
quarkus.http.auth.permission.roles2.paths=/crud/list
quarkus.http.auth.permission.roles2.policy=role-policy2
1 user ロールの SecurityIdentity インスタンスに、権限 see とアクション all を追加します。 同様に、 @PermissionsAllowed アノテーションの場合は、デフォルトで io.quarkus.security.StringPermission が使用されます。
2 権限 createupdate、および read は、ロール admin にマップされます。
3 ロールポリシー role-policy1 は、認証されたリクエストのみが /crud/modify および /crud/id サブパスにアクセスできるようにします。 パスマッチングアルゴリズムの詳細は、このガイドの 複数のパスのマッチング:最長パスが優先されます を参照してください。
4 java.security.Permission クラスのカスタム実装を指定できます。 カスタムクラスでは、権限名と、オプションでいくつかのアクション (たとえば、 String 配列) を受け入れるコンストラクターを 1 つだけ定義する必要があります。 このシナリオでは、権限 listnew CustomPermission ("list") として SecurityIdentity インスタンスに追加されます。

追加のコンストラクターパラメーターを使用してカスタム java.security.Permission クラスを作成することもできます。 これらの追加パラメーターの名前は、 @PermissionsAllowed アノテーションが付けられたメソッドの引数名と一致します。 その後、Quarkus は、 @PermissionsAllowed アノテーションが付けられたメソッドが呼び出された実際の引数を使用して、カスタム権限をインスタンス化します。

追加の引数を受け入れるカスタムの java.security.Permission クラスの例
package org.acme.library;

import java.security.Permission;
import java.util.Arrays;
import java.util.Set;

public class LibraryPermission extends Permission {

    private final Set<String> actions;
    private final Library library;

    public LibraryPermission(String libraryName, String[] actions, Library library) { (1)
        super(libraryName);
        this.actions = Set.copyOf(Arrays.asList(actions));
        this.library = library;
    }

    @Override
    public boolean implies(Permission requiredPermission) {
        if (requiredPermission instanceof LibraryPermission) {
            LibraryPermission that = (LibraryPermission) requiredPermission;
            boolean librariesMatch = getName().equals(that.getName());
            boolean requiredLibraryIsSublibrary = library.isParentLibraryOf(that.library);
            boolean hasOneOfRequiredActions = that.actions.stream().anyMatch(actions::contains);
            return (librariesMatch || requiredLibraryIsSublibrary) && hasOneOfRequiredActions;
        }
        return false;
    }

    // here comes your own implementation of the `java.security.Permission` class methods

    public static abstract class Library {

        protected String description;

        abstract boolean isParentLibraryOf(Library library);

    }

    public static class MediaLibrary extends Library {

        @Override
        boolean isParentLibraryOf(Library library) {
            return library instanceof MediaLibrary;
        }
    }

    public static class TvLibrary extends MediaLibrary {
        // TvLibrary specific implementation of the 'isParentLibraryOf' method
    }
}
1 カスタム Permission クラスのコンストラクターは 1 つだけ存在する必要があります。 最初のパラメーターは常に権限名とみなされ、 String 型である必要があります。 Quarkus はオプションでコンストラクターに権限アクションを渡すことができます。 これを実現するには、2 番目のパラメーターを String[] として宣言します。

LibraryPermission クラスは、 SecurityIdentityreadwrite、または list などの必要なアクションのいずれかを実行することを許可されている場合に、現在のライブラリーまたは親ライブラリーへのアクセスを許可します。

次の例は、 LibraryPermission クラスの使用方法を示しています。

package org.acme.library;

import io.quarkus.security.PermissionsAllowed;
import jakarta.enterprise.context.ApplicationScoped;
import org.acme.library.LibraryPermission.Library;

@ApplicationScoped
public class LibraryService {

    @PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class) (1)
    public Library updateLibrary(String newDesc, Library library) {
        library.description = newDesc;
        return library;
    }

    @PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class) (2)
    @PermissionsAllowed(value = {"tv:read", "tv:list"}, permission = LibraryPermission.class)
    public Library migrateLibrary(Library migrate, Library library) {
        // migrate libraries
        return library;
    }

}
1 仮パラメーター library は、同名の LibraryPermission コンストラクターパラメーターに一致するパラメーターとして識別されます。 そのため、Quarkus は library パラメーターを LibraryPermission クラスコンストラクターに渡します。 ただし、 updateLibrary メソッドが呼び出されるたびに LibraryPermission をインスタンス化する必要があります。
2 ここで、2 番目の Library パラメーターは名前 library と一致しますが、 migrate パラメーターは LibraryPermission 権限のインスタンス化中に無視されます。
LibraryPermission で保護されたリソースの例
package org.acme.library;

import io.quarkus.security.PermissionsAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import org.acme.library.LibraryPermission.Library;

@Path("/library")
public class LibraryResource {

    @Inject
    LibraryService libraryService;

    @PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class)
    @PUT
    @Path("/id/{id}")
    public Library updateLibrary(@PathParam("id") Integer id, Library library) {
        ...
    }

    @PUT
    @Path("/service-way/id/{id}")
    public Library updateLibrarySvc(@PathParam("id") Integer id, Library library) {
        String newDescription = "new description " + id;
        return libraryService.updateLibrary(newDescription, library);
    }

}

CRUDResource の例と同様に、次の例は、 admin ロールを持つユーザーに MediaLibrary を更新する権限を付与する方法を示しています。

package org.acme.library;

import io.quarkus.runtime.annotations.RegisterForReflection;

@RegisterForReflection (1)
public class MediaLibraryPermission extends LibraryPermission {

    public MediaLibraryPermission(String libraryName, String[] actions) {
        super(libraryName, actions, new MediaLibrary());    (2)
    }

}
1 ネイティブ実行可能ファイルを構築する場合、少なくとも 1 つの io.quarkus.security.PermissionsAllowed#name パラメーターでも使用されていない限り、権限クラスをリフレクション用に登録する必要があります。 @RegisterForReflection アノテーションの詳細は、native application tips ページを参照してください。
2 MediaLibrary インスタンスを LibraryPermission コンストラクターに渡します。
quarkus.http.auth.policy.role-policy3.permissions.admin=media-library:list,media-library:read,media-library:write   (1)
quarkus.http.auth.policy.role-policy3.permission-class=org.acme.library.MediaLibraryPermission
quarkus.http.auth.permission.roles3.paths=/library/*
quarkus.http.auth.permission.roles3.policy=role-policy3
1 media-library 権限を付与します。これにより、 readwrite、および list アクションが許可されます。 MediaLibraryTvLibrary クラスの親であるため、 admin ロールを持つユーザーも TvLibrary を変更することが許可されます。
/library/* パスは Keycloak プロバイダーの Dev UI ページからテストできます。これは、Dev Services for Keycloak によって自動的に作成されたユーザー aliceadmin ロールが付与されるためです。

これまでに示した例は、ロールと権限のマッピングを示しています。 SecurityIdentity インスタンスにプログラムで権限を追加することも可能です。 次の例では、SecurityIdentity がカスタマイズされ、以前に HTTP ロールベースポリシーで付与されたものと同じ権限が追加されます。

プログラムで SecurityIdentityLibraryPermission を追加する例
import java.security.Permission;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.security.identity.AuthenticationRequestContext;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.identity.SecurityIdentityAugmentor;
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
import io.smallrye.mutiny.Uni;

@ApplicationScoped
public class PermissionsIdentityAugmentor implements SecurityIdentityAugmentor {

    @Override
    public Uni<SecurityIdentity> augment(SecurityIdentity identity, AuthenticationRequestContext context) {
        if (isNotAdmin(identity)) {
            return Uni.createFrom().item(identity);
        }
        return Uni.createFrom().item(build(identity));
    }

    private boolean isNotAdmin(SecurityIdentity identity) {
        return identity.isAnonymous() || !"admin".equals(identity.getPrincipal().getName());
    }

    SecurityIdentity build(SecurityIdentity identity) {
        return QuarkusSecurityIdentity.builder(identity)
                .addPermission(new MediaLibraryPermission("media-library", new String[] { "read", "write", "list"}); (1)
                .build();
    }

}
1 作成された media-library 権限を追加すると、 readwrite、および list アクションを実行できます。 MediaLibraryTvLibrary クラスの親であるため、 admin ロールを持つユーザーも TvLibrary を変更することが許可されます。
jakarta.ws.rs.core.SecurityContext には権限がないため、アノテーションベースの権限はカスタム Jakarta REST SecurityContexts では機能しません。

権限チェッカーを作成する

デフォルトでは、 SecurityIdentity は、この ID が @PermissionAllowed 認可制限を通過するかどうかを確認するために使用できる権限で設定されている必要があります。または、 @PermissionChecker アノテーションを使用して、任意の CDI Bean メソッドを権限チェッカーとしてマークすることもできます。 @PermissionChecker アノテーション値は、 @PermissionsAllowed アノテーション値によって宣言された必要な権限と一致する必要があります。たとえば、権限チェッカーは次のように作成できます。

package org.acme.security.rest.resource;

import io.quarkus.security.PermissionChecker;
import io.quarkus.security.PermissionsAllowed;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;

import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.RestPath;

@Path("/project/{projectName}")
public class ProjectResource {

    @PermissionsAllowed("rename-project") (1)
    @POST
    public void renameProject(@RestPath String projectName, @RestForm String newName) {
        Project project = Project.findByName(projectName);
        project.name = newName;
    }

    @PermissionChecker("rename-project") (2)
    boolean canRenameProject(SecurityIdentity identity, String projectName) { (3) (4)
        var principalName = identity.getPrincipal().getName();
        var user = User.getUserByName(principalName);
        return userOwnsProject(projectName, user);
    }
}
1 ProjectResource#renameProject にアクセスするために必要な権限は、 rename-project 権限です。
2 ProjectResource#canRenameProject メソッドは、 ProjectResource#renameProject エンドポイントへのアクセスを承認します。
3 SecurityIdentity インスタンスは、任意の権限チェッカーメソッドに挿入できます。
4 この例では、 rename-project 権限チェッカーが同じリソースで宣言されています。ただし、次の例に示すように、この権限チェッカーを宣言できる場所に制限はありません。
権限チェッカーメソッドは、通常のスコープの CDI Bean または @Singleton Bean で宣言できます。 @Dependent CDI Bean スコープは現在サポートされていません。

上記の権限チェッカーは、 renameProject エンドポイントを認可するために SecurityIdentity インスタンスを必要とします。 rename-project 権限チェッカーをリソースに直接宣言する代わりに、この例のように任意の CDI Bean に宣言できます。

package org.acme.security.rest.resource;

import io.quarkus.security.PermissionChecker;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped (1)
public class ProjectPermissionChecker {

    @PermissionChecker("rename-project")
    boolean canRenameProject(String projectName, SecurityIdentity identity) {   (2)
        var principalName = identity.getPrincipal().getName();
        var user = User.getUserByName(principalName);
        return userOwnsProject(projectName, user);
    }

}
1 権限チェッカーを備えた CDI Bean は、通常のスコープ Bean または @Singleton Bean のいずれかである必要があります。
2 権限チェッカーメソッドは、 boolean または Uni<Boolean> を返す必要があります。プライベートチェッカーメソッドはサポートされていません。
権限チェックは、デフォルトでイベントループで実行されます。ワーカースレッドでチェックを実行したい場合は、権限チェッカーメソッドに io.smallrye.common.annotation.Blocking アノテーションを付与します。

@PermissionsAllowed 値と @PermissionChecker 値の一致は、次の例に示すように、文字列の等価性に基づいて行われます。

package org.acme.security;

import io.quarkus.security.PermissionChecker;
import io.quarkus.security.PermissionsAllowed;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class FileService {

    @PermissionsAllowed({ "delete:all", "delete:dir" }) (1)
    void deleteDirectory(Path directoryPath) {
        // delete directory
    }

    @PermissionsAllowed(value = { "delete:service", "delete:file" }, inclusive = true) (2)
    void deleteServiceFile(Path serviceFilePath) {
        // delete service file
    }

    @PermissionChecker("delete:all")
    boolean canDeleteAllDirectories(SecurityIdentity identity) {
        String filePermissions = identity.getAttribute("user-group-file-permissions");
        return filePermissions != null && filePermissions.contains("w");
    }

    @PermissionChecker("delete:service")
    boolean canDeleteService(SecurityIdentity identity) {
        return identity.hasRole("admin");
    }

    @PermissionChecker("delete:file")
    boolean canDeleteFile(Path serviceFilePath) {
        return serviceFilePath != null && !serviceFilePath.endsWith("critical");
    }
}
1 権限チェッカーメソッド canDeleteAllDirectories は、 delete:all 値が等しいため、 deleteDirectory へのアクセスを許可します。
2 権限チェッカーメソッドは正確に 2 つ必要です。1 つは delete:service 権限用、もう 1 つは delete:file 権限用です。

権限メタアノテーションを作成する

@PermissionsAllowed はメタアノテーションでも使用できます。たとえば、新しい @CanWrite セキュリティーアノテーションは次のように作成できます。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import io.quarkus.security.PermissionsAllowed;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@PermissionsAllowed(value = "write", permission = CustomPermission.class) (1)
public @interface CanWrite {

}
1 @CanWrite アノテーションが付けられたメソッドまたはクラスは、この @PermissionsAllowed アノテーションインスタンスにより保護されます。

@BeanParam パラメーターをカスタム権限に渡す

Quarkus は、保護されたメソッドパラメーターのフィールドをカスタム権限コンストラクターパラメーターにマップできます。この機能を使用すると、 jakarta.ws.rs.BeanParam パラメーターをカスタム権限に渡すことができます。次の Jakarta REST リソースを考えてみましょう。

package org.acme.security.rest.resource;

import io.quarkus.security.PermissionsAllowed;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

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

    @PermissionsAllowed(value = "say-hello", params = "beanParam.securityContext.userPrincipal.name") (1)
    @GET
    public String sayHello(@BeanParam SimpleBeanParam beanParam) {
        return "Hello from " + beanParam.uriInfo.getPath();
    }

}
1 params アノテーション属性は、ユーザープリンシパル名を BeanParamPermissionChecker#canSayHello メソッドに渡すことを指定します。 customAuthorizationHeaderquery などの他の BeanParamPermissionChecker#canSayHello メソッドパラメーターは自動的に一致します。Quarkus は、 beanParam フィールドとそのパブリックアクセサーの中から BeanParamPermissionChecker#canSayHello メソッドパラメーターを識別します。あいまいな解決を避けるため、自動検出は beanParam フィールドに対してのみ機能します。そのため、ユーザープリンシパル名へのパスを明示的に指定する必要がありました。

SimpleBeanParam クラスは以下の例のように宣言されています。

package org.acme.security.rest.dto;

import java.util.List;

import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.UriInfo;

public class SimpleBeanParam {

    @HeaderParam("CustomAuthorization")
    private String customAuthorizationHeader;

    @Context
    SecurityContext securityContext;

    @Context
    public UriInfo uriInfo;

    @QueryParam("query")
    public String query;    (1)

    public SecurityContext getSecurityContext() { (2)
        return securityContext;
    }

    public String customAuthorizationHeader() { (3)
        return customAuthorizationHeader;
    }
}
1 Quarkus Security は、カスタム権限コンストラクターにパブリックフィールドのみを渡すことができます。
2 Quarkus Security は、パブリックゲッターメソッドが利用可能な場合はそれを自動的に使用します。
3 customAuthorizationHeader フィールドはパブリックではないため、Quarkus は customAuthorizationHeader アクセサーを使用してこのフィールドにアクセスします。これは、生成されたアクセサーに接頭辞 get が付かない Java レコードで特に便利です。

以下は、ユーザープリンシパル、カスタムヘッダー、およびクエリーパラメーターに基づいて say-hello 権限をチェックする @PermissionChecker メソッドの例です。

package org.acme.security.permission;

import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.security.PermissionChecker;

@ApplicationScoped
public class BeanParamPermissionChecker {

    @PermissionChecker("say-hello")
    boolean canSayHello(String customAuthorizationHeader, String name, String query) {
        boolean queryParamAllowedForPermissionName = checkQueryParams(query);
        boolean usernameWhitelisted = isUserNameWhitelisted(name);
        boolean customAuthorizationMatches = checkCustomAuthorization(customAuthorizationHeader);
        return queryParamAllowedForPermissionName && usernameWhitelisted && customAuthorizationMatches;
    }

    ...
}
@BeanParam@PermissionChecker メソッドに直接渡し、プログラムでそのフィールドにアクセスできます。 @PermissionsAllowed#params 属性を使用して @BeanParam フィールドを参照する機能は、異なる構造の @BeanParam クラスが複数ある場合に便利です。

関連コンテンツ