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


Red Hat build of Quarkus には、REST エンドポイントと CDI Bean 上の共通セキュリティーアノテーション @RolesAllowed@DenyAll@PermitAll に基づく ロールベースのアクセス制御 (RBAC) を可能にするセキュリティーが組み込まれています。

Expand
表1.2 Red Hat build of Quarkus のアノテーションタイプの概要
アノテーションタイプ説明

@DenyAll

どのセキュリティーロールも指定のメソッドを呼び出すことができないことを指定します。

@PermitAll

すべてのセキュリティーロールが指定のメソッドを呼び出せることを指定します。

@PermitAll は認証なしですべてのユーザーにアクセスを許可します。

@RolesAllowed

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

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

次の SubjectExposingResource の例は、エンドポイントの記述および保護に Jakarta REST と Common Security アノテーションの両方を使用するエンドポイントを示しています。

SubjectExposingResource の例

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("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("unsecured")
    @PermitAll 
3

    public String getSubjectUnsecured(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal(); 
4

        String name = user != null ? user.getName() : "anonymous";
        return name;
    }

    @GET
    @Path("denied")
    @DenyAll 
5

    public String getSubjectDenied(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal();
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }
}
Copy to Clipboard Toggle word wrap

1
/subject/secured エンドポイントは、@RolesAllowed("Tester") アノテーションを使用して "Tester" ロールが付与された認証済みユーザーを必要とします。
2
エンドポイントは、Jakarta REST SecurityContext からユーザープリンシパルを取得します。これは、保護されたエンドポイントに対して non-null を返します。
3
/subject/unsecured エンドポイントは、@PermitAll アノテーションを指定することにより、認証されていないアクセスを許可します。
4
ユーザープリンシパルを取得する呼び出しは、呼び出し元が認証されていない場合は null を返し、呼び出し元が認証されている場合は non-null を返します。
5
/subject/denied エンドポイントは、@DenyAll アノテーションを宣言することにより、呼び出し元のユーザーに関係なく、REST メソッドとしての直接アクセスをすべて禁止します。このメソッドは、このクラスの他のメソッドによって内部的に呼び出すことができます。
Important

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

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

Expand
表1.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
Copy to Clipboard Toggle word wrap

サブジェクトアクセス制御の例

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("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;
    }
}
Copy to Clipboard Toggle word wrap

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

1.2.1. 権限のアノテーション

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;
        }
    }
}
Copy to Clipboard Toggle word wrap

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() を使用してプログラム的に取得することです。
Important

IO スレッドで @PermissionsAllowed を使用する予定の場合は、プロアクティブ認証 の情報を確認してください。

注記

Quarkus インターセプターの制限により、@PermissionsAllowed をクラスレベルで繰り返すことはできません。詳細は、Quarkus "CDI reference" ガイドの 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
Copy to Clipboard Toggle word wrap
1
user ロールの SecurityIdentity インスタンスに、see 権限と all アクションを追加します。同様に、@PermissionsAllowed アノテーションには、io.quarkus.security.StringPermission がデフォルトで使用されます。
2
create 権限、update 権限、および 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
    }
}
Copy to Clipboard Toggle word wrap

1
カスタムの Permission クラスのコンストラクターは、1 つだけである必要があります。最初のパラメーターは、常に権限名とみなされ、String 型である必要があります。Quarkus は、必要に応じてコンストラクターに権限アクションを渡すことができます。これを実現するには、2 番目のパラメーターを String[] として宣言します。

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

次の例は、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 update) {
        update.description = newDesc;
        return update;
    }

    @PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class, params = "library") 
2

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

}
Copy to Clipboard Toggle word wrap
1
正式なパラメーターの update は、最初の Library パラメーターとして識別され、LibraryPermission クラスに渡されます。ただし、updateLibrary メソッドを呼び出すたびに LibraryPermission をインスタンス化する必要があります。
2
ここで、最初の Library パラメーターは migrate です。したがって、library パラメーターは、PermissionsAllowed#params によって明示的にマークされます。権限コンストラクターとアノテーション付きメソッドには、library パラメーターが設定されている必要があります。設定しないと、検証が失敗します。

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);
    }

}
Copy to Clipboard Toggle word wrap

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

    }

}
Copy to Clipboard Toggle word wrap
1
ネイティブ実行可能ファイルをビルドする場合は、権限クラスをリフレクション用に登録する必要があります。ただし、1 つ以上の io.quarkus.security.PermissionsAllowed#name パラメーターでもその権限クラスが使用されている場合を除きます。
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
Copy to Clipboard Toggle word wrap
1
readwrite、および list アクションを許可する権限 media-library を付与します。MediaLibraryTvLibrary クラスの親であるため、admin ロールを持つユーザーも TvLibrary を変更することが許可されます。
ヒント

/library/* パスは、Keycloak プロバイダーの Dev UI ページからテストできます。Dev Services for Keycloak によって自動的に作成されるユーザー alice が、admin ロールを持っているためです。

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

プログラムで SecurityIdentityLibraryPermission を追加する例

import java.security.Permission;
import java.util.function.Function;

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) {
        Permission possessedPermission = new MediaLibraryPermission("media-library",
                new String[] { "read", "write", "list"}); 
1

        return QuarkusSecurityIdentity.builder(identity)
                .addPermissionChecker(new Function<Permission, Uni<Boolean>>() { 
2

                    @Override
                    public Uni<Boolean> apply(Permission requiredPermission) {
                        boolean accessGranted = possessedPermission.implies(requiredPermission);
                        return Uni.createFrom().item(accessGranted);
                    }
                })
                .build();
    }

}
Copy to Clipboard Toggle word wrap

1
作成した権限 media-library は、readwrite、および list アクションを実行できます。MediaLibraryTvLibrary クラスの親であるため、admin ロールを持つユーザーも TvLibrary を変更することが許可されます。
2
io.quarkus.security.runtime.QuarkusSecurityIdentity.Builder#addPermissionChecker により権限チェッカーを追加できます。
Important

アノテーションベースの権限は、カスタムの Jakarta REST SecurityContexts では機能しません。jakarta.ws.rs.core.SecurityContext に権限がないためです。

Red Hat logoGithubredditYoutubeTwitter

詳細情報

試用、購入および販売

コミュニティー

Red Hat ドキュメントについて

Red Hat をお使いのお客様が、信頼できるコンテンツが含まれている製品やサービスを活用することで、イノベーションを行い、目標を達成できるようにします。 最新の更新を見る.

多様性を受け入れるオープンソースの強化

Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。このような変更は、段階的に実施される予定です。詳細情報: Red Hat ブログ.

会社概要

Red Hat は、企業がコアとなるデータセンターからネットワークエッジに至るまで、各種プラットフォームや環境全体で作業を簡素化できるように、強化されたソリューションを提供しています。

Theme

© 2026 Red Hat
トップに戻る