Quarkus权限角色映射:注解混用报错,如何用注解实现指南效果?
问题描述
根据Quarkus官方指南,可通过配置文件将权限映射至角色,示例代码与配置如下:
注解示例代码
@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; }
配置文件示例
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
配置说明
为拥有
user角色的SecurityIdentity添加权限see:all,默认使用io.quarkus.security.StringPermission,和@PermissionsAllowed注解逻辑一致。
权限create、update、read被映射到admin角色。
但同时使用@PermissionsAllowed和@RolesAllowed注解时触发程序错误,相关代码及报错信息如下:
出错代码
public class ReadyResource { @Inject private ApplicationConfig applicationConfig; @GET() @Path("ready2") @PermissionsAllowed("read") @RolesAllowed("User") @Produces(MediaType.APPLICATION_JSON) public Result<ApplicationConfig> getReady2() { return Result.ofSuccess(applicationConfig); } }
错误信息
2023-05-14 13:19:05,464 ERROR [io.qua.dep.dev.IsolatedDevModeMain] (main) Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors [error]: Build step io.quarkus.security.deployment.SecurityProcessor#gatherSecurityChecks threw an exception: java.lang.IllegalStateException: Method hello of class org.acme.GreetingResource2 is annotated with multiple security annotations at io.quarkus.security.deployment.PermissionSecurityChecks$PermissionSecurityChecksBuilder.gatherPermissionsAllowedAnnotations(PermissionSecurityChecks.java:216) at io.quarkus.security.deployment.SecurityProcessor.gatherSecurityAnnotations(SecurityProcessor.java:673) at io.quarkus.security.deployment.SecurityProcessor.gatherSecurityChecks(SecurityProcessor.java:527) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) at java.base/java.lang.reflect.Method.invoke(Method.java:578) at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:909) at io.quarkus.builder.BuildContext.run(BuildContext.java:282) at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18) at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538) at java.base/java.lang.Thread.run(Thread.java:1623) at org.jboss.threads.JBossThread.run(JBossThread.java:501)
疑问:
- 为何配置文件方式运行正常,但混用两种注解会报错?
- 如何仅通过注解实现指南中的权限与角色映射效果?
解答
1. 混用注解报错原因
Quarkus的安全部署处理器不允许在同一个方法上同时使用@PermissionsAllowed和@RolesAllowed这类安全注解,因为二者对应不同的安全校验逻辑:
@RolesAllowed是基于角色的直接校验,仅判断当前用户是否拥有指定角色@PermissionsAllowed是基于权限的校验,判断当前用户是否拥有指定权限
配置文件方式是将角色和权限做映射,本质是让对应角色的用户自动具备指定权限,最终统一通过权限逻辑校验,不存在冲突;但直接混用两种注解会让处理器无法确定采用哪种校验逻辑,因此抛出multiple security annotations错误。
2. 仅用注解实现权限-角色映射的方案
要实现配置文件的角色-权限映射效果,无需混用注解,只需使用@PermissionsAllowed,再通过自定义角色权限映射器关联角色与权限:
步骤1:实现PermissionMapper接口
创建自定义权限映射器,将用户角色转换为对应权限:
import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.runtime.PermissionMapper; import jakarta.enterprise.context.ApplicationScoped; import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; @ApplicationScoped public class RoleToPermissionMapper implements PermissionMapper { @Override public Set<String> mapPermissions(SecurityIdentity identity) { // 根据用户角色映射对应权限 return identity.getRoles().stream() .flatMap(role -> switch (role) { case "user" -> Set.of("see:all").stream(); case "admin" -> Set.of("create", "update", "read").stream(); default -> Collections.emptySet().stream(); }) .collect(Collectors.toSet()); } }
步骤2:仅使用@PermissionsAllowed注解
在接口方法上只保留@PermissionsAllowed,无需添加@RolesAllowed:
public class ReadyResource { @Inject private ApplicationConfig applicationConfig; @GET() @Path("ready2") @PermissionsAllowed("read") // 仅指定权限,映射由自定义mapper处理 @Produces(MediaType.APPLICATION_JSON) public Result<ApplicationConfig> getReady2() { return Result.ofSuccess(applicationConfig); } }
这样,当拥有admin角色的用户访问该接口时,自定义映射器会自动为其添加read权限,从而通过@PermissionsAllowed的校验,实现和配置文件相同的效果。
内容的提问来源于stack exchange,提问作者JackyAnn

