为何Quarkus自定义注解中的内置@RolesAllowed会被忽略?
解决方案
原生@RolesAllowed注解不支持通过元注解传递权限规则——权限校验器只会直接扫描目标类/方法上的@RolesAllowed,不会解析自定义注解上的该元注解,这就是导致权限校验失效的原因。以下是几种可行的实现方式:
方式一:利用Spring的@AliasFor(Spring环境下推荐)
如果项目基于Spring/Spring Security,可以通过@AliasFor让自定义注解与@RolesAllowed的属性绑定,Spring的权限处理器会自动识别这种元注解的属性继承:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Inherited @RolesAllowed // 保留原注解作为元注解 public @interface RolesAllowedAdminArea { // 用@AliasFor绑定到@RolesAllowed的value属性 @AliasFor(annotation = RolesAllowed.class, attribute = "value") String[] value() default {"role1", "role2", "role3"}; }
使用@RolesAllowedAdminArea的类/方法,会被Spring Security当作带有对应角色列表的@RolesAllowed处理,无需额外修改校验逻辑。
方式二:自定义权限拦截器(原生JEE或非Spring环境)
如果是原生JEE环境,需要自己实现权限拦截器,递归解析目标类/方法的注解(包括自定义注解上的元注解),提取@RolesAllowed的角色列表进行校验:
1. 实现拦截器
@Interceptor @Priority(Priorities.AUTHORIZATION) public class CustomRolesInterceptor { @AroundInvoke public Object checkAuthorization(InvocationContext ctx) throws Exception { // 获取目标方法和类 Method targetMethod = ctx.getMethod(); Class<?> targetClass = ctx.getTarget().getClass(); // 先从方法注解中提取所需角色,方法优先级高于类 Set<String> requiredRoles = extractRequiredRoles(targetMethod.getAnnotations()); if (requiredRoles.isEmpty()) { requiredRoles = extractRequiredRoles(targetClass.getAnnotations()); } if (!requiredRoles.isEmpty()) { // 获取当前用户权限(根据实际环境调整,这里用JEE SecurityContext示例) Principal principal = SecurityContext.getContext().getUserPrincipal(); if (principal == null || !(principal instanceof UserPrincipal)) { throw new AccessDeniedException("未授权访问"); } UserPrincipal userPrincipal = (UserPrincipal) principal; boolean hasPermission = userPrincipal.getRoles().stream() .anyMatch(role -> requiredRoles.contains(role)); if (!hasPermission) { throw new AccessDeniedException("缺少必要角色"); } } return ctx.proceed(); } // 递归提取注解(包括元注解)中的@RolesAllowed角色 private Set<String> extractRequiredRoles(Annotation[] annotations) { Set<String> roles = new HashSet<>(); for (Annotation annotation : annotations) { // 直接处理@RolesAllowed注解 if (annotation instanceof RolesAllowed) { Collections.addAll(roles, ((RolesAllowed) annotation).value()); } // 检查当前注解的元注解中是否有@RolesAllowed RolesAllowed metaRoles = annotation.annotationType().getAnnotation(RolesAllowed.class); if (metaRoles != null) { Collections.addAll(roles, metaRoles.value()); } } return roles; } }
2. 注册拦截器
在beans.xml中配置拦截器绑定:
<interceptors> <interceptor-binding> <target-class>你的包路径..*</target-class> <interceptor-class>你的包路径.CustomRolesInterceptor</interceptor-class> </interceptor-binding> </interceptors>
方式三:自定义GraphQL权限校验器(针对@GraphQLApi场景)
因为你的类标注了@GraphQLApi,可以针对GraphQL的请求流程自定义权限拦截器,在数据获取前校验角色:
@Component public class GraphQLRolesInterceptor implements DataFetcherInterceptor { @Override public DataFetcherResult<?> intercept(DataFetcherInvocation invocation) throws Exception { Method targetMethod = invocation.getMethod(); Class<?> targetClass = targetMethod.getDeclaringClass(); Set<String> requiredRoles = extractRequiredRoles(targetMethod.getAnnotations()); if (requiredRoles.isEmpty()) { requiredRoles = extractRequiredRoles(targetClass.getAnnotations()); } if (!requiredRoles.isEmpty()) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth == null || !auth.isAuthenticated()) { throw new AccessDeniedException("未授权访问"); } boolean hasRole = auth.getAuthorities().stream() .anyMatch(authority -> requiredRoles.contains(authority.getAuthority())); if (!hasRole) { throw new AccessDeniedException("缺少必要角色"); } } return invocation.proceed(); } private Set<String> extractRequiredRoles(Annotation[] annotations) { Set<String> roles = new HashSet<>(); for (Annotation ann : annotations) { if (ann instanceof RolesAllowed) { Collections.addAll(roles, ((RolesAllowed) ann).value()); } RolesAllowed metaAnn = ann.annotationType().getAnnotation(RolesAllowed.class); if (metaAnn != null) { Collections.addAll(roles, metaAnn.value()); } } return roles; } }
注册该拦截器后,GraphQL请求在处理对应字段前会自动执行权限校验。
内容的提问来源于stack exchange,提问作者Dejan
相关产品推荐
相关产品推荐

