1.2. アノテーションを使用した認可
Red Hat build of Quarkus には、REST エンドポイントと CDI Bean 上の共通セキュリティーアノテーション @RolesAllowed、@DenyAll、@PermitAll に基づく ロールベースのアクセス制御 (RBAC) を可能にするセキュリティーが組み込まれています。
| アノテーションタイプ | 説明 |
|---|---|
|
| どのセキュリティーロールも指定のメソッドを呼び出すことができないことを指定します。 |
|
| すべてのセキュリティーロールが指定のメソッドを呼び出せることを指定します。
|
|
| アプリケーション内のメソッドへのアクセスを許可するセキュリティーロールのリストを指定します。 |
|
|
Red Hat build of Quarkus は、認証されたすべてのユーザーがリソースにアクセスできるようにする |
|
| 指定されたメソッドを呼び出すことが許可される権限のリストを指定します。 |
|
|
指定された Jakarta REST エンドポイントへのアクセスを許可する名前付きの |
次の SubjectExposingResource の例は、エンドポイントの記述および保護に Jakarta REST と Common Security アノテーションの両方を使用するエンドポイントを示しています。
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")
public String getSubjectSecured(@Context SecurityContext sec) {
Principal user = sec.getUserPrincipal();
String name = user != null ? user.getName() : "anonymous";
return name;
}
@GET
@Path("authenticated")
@Authenticated
public String getSubjectAuthenticated(@Context SecurityContext sec) {
Principal user = sec.getUserPrincipal();
String name = user != null ? user.getName() : "anonymous";
return name;
}
@GET
@Path("unsecured")
@PermitAll
public String getSubjectUnsecured(@Context SecurityContext sec) {
Principal user = sec.getUserPrincipal();
String name = user != null ? user.getName() : "anonymous";
return name;
}
@GET
@Path("denied")
@DenyAll
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 アノテーション値は、デフォルト値やネストされたプロパティー式を含む プロパティー式 をサポートします。アノテーションで使用される設定プロパティーは実行時に解決されます。
| アノテーション | 値の説明 |
|---|---|
|
|
エンドポイントは、 |
|
| 値に複数の変数を含めることができることを示す例。 |
|
|
デフォルト値の例示。必要なロールを、 |
@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}")
public String getSubjectSecuredAdmin(@Context SecurityContext sec) {
return getUsername(sec);
}
@GET
@Path("software-tester")
@RolesAllowed("${tester.group}-${tester.role}")
public String getSubjectSoftwareTester(@Context SecurityContext sec) {
return getUsername(sec);
}
@GET
@Path("user")
@RolesAllowed("${customer:User}")
public String getSubjectUser(@Context SecurityContext sec) {
return getUsername(sec);
}
@GET
@Path("secured")
@RolesAllowed("${secured}")
public String getSubjectSecured(@Context SecurityContext sec) {
return getUsername(sec);
}
@GET
@Path("list")
@RolesAllowed("${all-roles}")
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エンドポイントには、@RolesAllowed("${customer:User}")アノテーションを使用して "User" ロールが付与された認証済みユーザーが必要です。これは、設定プロパティーcustomerを設定しなかったためです。 - 4
- 実稼働環境では、この
/subject/securedエンドポイントには、Userロールを持つ認証済みユーザーが必要です。開発モードでは、すべての認証済みユーザーが許可されます。 - 5
- プロパティー式
all-rolesは、コレクション型Listとして扱われます。そのため、このエンドポイントには、Administrator、Software、Tester、Userの各ロールがアクセスできるようになります。
1.2.1. エンドポイントセキュリティーアノテーションと Jakarta REST 継承 リンクのコピーリンクがクリップボードにコピーされました!
Quarkus は、次の例のようにエンドポイント実装またはそのクラスに配置されたセキュリティーアノテーションをサポートしています。
@Path("hello")
public interface HelloInterface {
@GET
String hello();
}
@DenyAll
public class HelloInterfaceImpl implements HelloInterface {
@RolesAllowed("admin")
@Override
public String hello() {
return "Hello";
}
}
デフォルトのインターフェイスメソッドとして宣言された 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();
}
}
1.2.2. 権限のアノテーション リンクのコピーリンクがクリップボードにコピーされました!
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")
@PermissionsAllowed("update")
@POST
@Path("/modify/repeated")
public String createOrUpdate() {
return "modified";
}
@PermissionsAllowed(value = {"create", "update"}, inclusive=true)
@POST
@Path("/modify/inclusive")
public String createOrUpdate(Long id) {
return id + " modified";
}
@PermissionsAllowed({"see:detail", "see:all", "read"})
@GET
@Path("/id/{id}")
public String getItem(String id) {
return "item-detail-" + id;
}
@PermissionsAllowed(value = "list", permission = CustomPermission.class)
@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();
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
SecurityIdentityにread権限またはsee権限と、allアクションまたはdetailアクションのどちらかがある場合は、getItemへのアクセスが許可されます。- 4
- 任意の
java.security.Permission実装を使用できます。デフォルトでは、文字列ベースの権限はio.quarkus.security.StringPermissionにより実行されます。 - 5
- 権限は Bean ではないため、Bean インスタンスを取得する唯一の方法は、
Arc.container()を使用してプログラム的に取得することです。
IO スレッドで @PermissionsAllowed を使用する予定の場合は、プロアクティブ認証 の情報を確認してください。
Quarkus インターセプターの制限により、@PermissionsAllowed をクラスレベルで繰り返すことはできません。詳細は、Quarkus の「CDI reference」ガイドの Repeatable interceptor bindings セクションを参照してください。
ロールが有効な SecurityIdentity インスタンスに権限を追加する最も簡単な方法は、ロールを権限にマッピングすることです。次の例に示すように、設定を使用した認可 を使用して、認可されたリクエストに、CRUDResource エンドポイントに必要な SecurityIdentity 権限を付与します。
quarkus.http.auth.policy.role-policy1.permissions.user=see:all
quarkus.http.auth.policy.role-policy1.permissions.admin=create,update,read
quarkus.http.auth.permission.roles1.paths=/crud/modify/*,/crud/id/*
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
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
create権限、update権限、およびread権限が、adminロールにマッピングされます。- 3
- ロールポリシー
role-policy1は、認証されたリクエストのみが/crud/modifyおよび/crud/idサブパスにアクセスできるようにします。パスマッチングアルゴリズムの詳細は、このガイドで後述する 複数のパスのマッチング: 最長パスが優先される を参照してください。 - 4
java.security.Permissionクラスのカスタム実装を指定できます。カスタムクラスでは、権限名とアクション (任意) を受け入れるコンストラクター (String配列など) を 1 つだけ定義する必要があります。この場合は、権限listがnew 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) {
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 クラスは、SecurityIdentity が read、write、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)
public Library updateLibrary(String newDesc, Library library) {
library.description = newDesc;
return library;
}
@PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class)
@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
public class MediaLibraryPermission extends LibraryPermission {
public MediaLibraryPermission(String libraryName, String[] actions) {
super(libraryName, actions, new MediaLibrary());
}
}
- 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
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
read、write、およびlistアクションを許可する権限media-libraryを付与します。MediaLibraryはTvLibraryクラスの親であるため、adminロールを持つユーザーもTvLibraryを変更することが許可されます。
/library/* パスは、Keycloak プロバイダーの Dev UI ページからテストできます。Dev Services for Keycloak により自動的に作成されるユーザー alice が、admin ロールを持っているためです。
これまでに示した例は、ロールと権限のマッピングを示しています。SecurityIdentity インスタンスには、プログラムで権限を追加することも可能です。次の例では、SecurityIdentity をカスタマイズ して、以前に HTTP ロールベースのポリシーで付与したものと同じ権限を追加します。
プログラムで SecurityIdentity に LibraryPermission を追加する例
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"});
.build();
}
}
- 1
- 作成された
media-library権限を追加すると、read、write、およびlistアクションを実行できます。MediaLibraryはTvLibraryクラスの親であるため、adminロールを持つユーザーもTvLibraryを変更することが許可されます。
アノテーションベースの権限は、カスタムの Jakarta REST SecurityContexts では機能しません。jakarta.ws.rs.core.SecurityContext に権限がないためです。
1.2.2.1. 権限チェッカーを作成する リンクのコピーリンクがクリップボードにコピーされました!
デフォルトでは、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")
@POST
public void renameProject(@RestPath String projectName, @RestForm String newName) {
Project project = Project.findByName(projectName);
project.name = newName;
}
@PermissionChecker("rename-project")
boolean canRenameProject(SecurityIdentity identity, String projectName) {
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
public class ProjectPermissionChecker {
@PermissionChecker("rename-project")
boolean canRenameProject(String projectName, SecurityIdentity identity) {
var principalName = identity.getPrincipal().getName();
var user = User.getUserByName(principalName);
return userOwnsProject(projectName, user);
}
}
権限チェックは、デフォルトでイベントループで実行します。ワーカースレッドでチェックを実行する場合は、権限チェッカーメソッドに 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" })
void deleteDirectory(Path directoryPath) {
// delete directory
}
@PermissionsAllowed(value = { "delete:service", "delete:file" }, inclusive = true)
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.2.2.2. 権限メタアノテーションを作成する リンクのコピーリンクがクリップボードにコピーされました!
@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)
public @interface CanWrite {
}
- 1
@CanWriteアノテーションが付けられたメソッドまたはクラスは、この@PermissionsAllowedアノテーションインスタンスにより保護されます。
1.2.2.3. @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")
@GET
public String sayHello(@BeanParam SimpleBeanParam beanParam) {
return "Hello from " + beanParam.uriInfo.getPath();
}
}
- 1
paramsアノテーション属性は、ユーザープリンシパル名をBeanParamPermissionChecker#canSayHelloメソッドに渡す必要があることを指定します。customAuthorizationHeaderやqueryなどの他の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;
public SecurityContext getSecurityContext() {
return securityContext;
}
public String customAuthorizationHeader() {
return customAuthorizationHeader;
}
}
以下は、ユーザープリンシパル、カスタムヘッダー、クエリーパラメーターに基づいて 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 クラスがある場合に便利です。