You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 11:37:51