Spring中用AspectJ拦截Mapper方法内Setter实现字段权限控制可行性问询
在Spring中用AspectJ环绕fetchA()内的setter实现字段权限控制可行吗?
当然可行!不过要明确:Spring AOP的代理机制(不管是JDK动态代理还是CGLIB)只能拦截外部对代理对象的方法调用,没法深入到方法内部的执行步骤,所以要实现拦截fetchA()方法内部的setter调用,必须用原生AspectJ的编译时/加载时织入能力,而不是Spring AOP。
下面给你拆解具体的实现思路和步骤:
一、核心原理
AspectJ是基于字节码织入的AOP框架,它能直接修改目标类的字节码,所以可以拦截方法内部的任意方法调用——这正是你需要的:在fetchA()执行过程中,拦截它对a.setB()、a.setC()、a.setD()的调用,根据权限决定是否执行setter(或者说是否把字段设为null)。
二、具体实现步骤
1. 配置AspectJ环境
先把AspectJ的依赖加到项目里,以Maven为例:
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.20.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.20.1</version> </dependency>
如果是Spring Boot项目,还需要开启加载时织入(LTW),在application.properties里加:
spring.aop.aspectj.autoproxy=true spring.aspectj.weaver.load-time-weaving.enabled=true
2. 编写AspectJ切面拦截setter
我们需要精准定位:只有在被@FieldPermissionAnnotation("a")标注的fetchA()方法内部,调用A类的setter时才触发拦截逻辑。示例切面代码如下:
@Aspect @Component public class FieldPermissionAspect { // 模拟从数据库获取的用户权限字段列表,实际可以从缓存/数据库动态获取 private List<String> userPermittedFields = Arrays.asList("a-b-e", "a-b-f", "a-c", "a-d"); // 切入点:匹配被@FieldPermissionAnnotation标注的方法内部,调用A类的setter的场景 @Pointcut("@annotation(fieldPermissionAnno) && execution(* com.yourpackage.A.set*(..))") public void aSetterInPermittedMethod(FieldPermissionAnnotation fieldPermissionAnno) {} @Around("aSetterInPermittedMethod(fieldPermissionAnno)") public Object aroundSetterInvocation(ProceedingJoinPoint joinPoint, FieldPermissionAnnotation fieldPermissionAnno) throws Throwable { // 从setter方法名推导字段名:比如setB -> b,setC -> c String setterMethodName = joinPoint.getSignature().getName(); String fieldName = setterMethodName.substring(3, 4).toLowerCase() + setterMethodName.substring(4); // 拼接权限路径:比如"a" + "-" + "b" = "a-b" String permissionPath = fieldPermissionAnno.value() + "-" + fieldName; // 检查用户是否有该字段的权限:没有权限就返回null,相当于不执行setter(字段保持null) if (!userPermittedFields.contains(permissionPath)) { return null; } // 有权限则执行原setter方法 return joinPoint.proceed(); } }
3. 关键细节说明
- 切入点的精准性:AspectJ的
execution(* com.yourpackage.A.set*(..))能匹配A类的所有setter,再结合@annotation(fieldPermissionAnno),就只会拦截那些在被@FieldPermissionAnnotation标注的方法内部的setter调用——这正是Spring AOP做不到的内部方法拦截。 - 权限判断逻辑:通过setter方法名推导字段名,再结合注解的
value(这里是"a")拼接成权限路径,和数据库返回的权限列表对比。如果没有权限,直接返回null,这样原setter就不会把值赋给字段,相当于字段被设为null。 - 织入方式选择:如果是传统项目可以用编译时织入(CTW),需要配置AspectJ Maven插件;Spring Boot项目用加载时织入(LTW)更方便,开启配置就行。
三、额外注意事项
- 确保
fetchB()、fetchC()、fetchD()的执行不受切面影响——我们只拦截setter的调用,不会干扰这些数据查询方法的执行。 - 权限列表建议做成动态的,比如从数据库查询后缓存起来,避免每次拦截都查库影响性能。
- 测试时可以通过查看编译后的字节码,或者打印日志来验证切面是否生效。
内容的提问来源于stack exchange,提问作者Lakshya Yadav
相关产品推荐
相关产品推荐

