Spring中接口类上的@PreAuthorize注解无效,如何实现方法双权限控制?
嘿,这个问题我之前也踩过坑——Spring Security默认的方法安全注解处理器只会盯着实现类的类和方法注解,完全忽略接口上的(不管是接口类还是接口方法上的@PreAuthorize都会被跳过)。这是因为默认用的是JDK动态代理,它只关注实现类的方法元数据。不过别担心,有两种简单的办法能解决这个问题:
方案一:切换到CGLIB代理(最省心)
这种方法零侵入,只需要修改你的Spring Security方法安全配置类,开启CGLIB代理就行:
@Configuration @EnableGlobalMethodSecurity( prePostEnabled = true, proxyTargetClass = true // 开启基于类的CGLIB代理 ) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { // 啥额外代码都不用写,默认的注解处理器会自动读取接口上的注解 }
开启proxyTargetClass = true后,Spring会生成目标类的子类代理(而不是JDK的接口代理),这时候接口类上的@PreAuthorize会被当成类级别的权限要求,接口方法上的注解则是方法级的——两者会自动组合成AND关系,正好符合你的需求:用户必须同时拥有interface和test权限才能访问testMethod。
⚠️ 小提醒:如果你的实现类是final的,CGLIB代理会失效,这种情况就换方案二。
方案二:自定义元数据处理器(更灵活)
要是不能用CGLIB代理,你可以自定义一个MethodSecurityMetadataSource,让它主动去读取接口上的注解:
第一步:写自定义元数据源类
继承Spring Security的AnnotationMethodSecurityMetadataSource,重写getAttributes方法来检查接口的注解:
class InterfaceAwareAnnotationMetadataSource extends AnnotationMethodSecurityMetadataSource { public InterfaceAwareAnnotationMetadataSource() { // 指定要处理的Spring Security注解类型 super(PreAuthorize.class, PostAuthorize.class, PreFilter.class, PostFilter.class); } @Override public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) { // 先遍历实现类的所有接口 for (Class<?> intf : targetClass.getInterfaces()) { // 检查接口类上的注解 Collection<ConfigAttribute> classAttrs = super.getAttributes(null, intf); // 检查接口对应方法上的注解 try { Method interfaceMethod = intf.getMethod(method.getName(), method.getParameterTypes()); Collection<ConfigAttribute> methodAttrs = super.getAttributes(interfaceMethod, intf); // 组合类和方法的注解(默认是AND关系,符合你的需求) if (!classAttrs.isEmpty() && !methodAttrs.isEmpty()) { classAttrs.addAll(methodAttrs); return classAttrs; } else if (!classAttrs.isEmpty()) { return classAttrs; } else if (!methodAttrs.isEmpty()) { return methodAttrs; } } catch (NoSuchMethodException e) { // 接口没这个方法?跳过继续检查下一个 continue; } } // 接口没找到注解?再检查实现类本身的 return super.getAttributes(method, targetClass); } }
第二步:配置使用自定义元数据源
在你的方法安全配置类里指定用这个自定义的元数据源:
@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { @Override protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() { return new InterfaceAwareAnnotationMetadataSource(); } }
这个方法不管是JDK代理还是CGLIB代理都能生效,而且你还能自己调整注解的组合逻辑(比如改成OR关系,不过你的需求是AND,默认逻辑就够了)。
验证一下
不管用哪种方案,你测试一下:
- 只有
interface权限?访问testMethod会被拒绝 - 只有
test权限?同样被拒绝 - 同时拥有两个权限?顺利调用返回
testing
完全符合你的要求~
内容的提问来源于stack exchange,提问作者mazenaissa

