在这篇博客中,我将分享一个近期在排查线上问题时,意外发现的权限控制实现方式。起初,这个问题让我迷惑不已,因为在前端请求中,有个权限控制参数为 null,但在请求从网关进入微服务后,该参数神秘地有了值。经过一番深入探究,我找到了答案:原来这背后是一段优雅的切点代码。
问题背景
最近在排查一个线上问题时,前端调用的请求体中,有个权限控制的参数为null, 但是请求从网关进入微服务中之后,这个参数却莫名其妙地有了值。
起初,我反复检查了代码,试图找出这个参数何时被赋值,但始终未果。直觉告诉我,这可能与切点相关。切点可以在方法执行前后动态地插入代码片段,于是我决定证实这个猜想。
验证过程
带着这个猜想,我在项目中全局搜索了 @Pointcut 和 @Before 注解。果然,发现了一段与权限控制相关的切面代码。在这段代码中,看到了对于权限参数的赋值逻辑,这就解释了参数为何在进入微服务后有值。
思路解析
下面,我将对这段权限控制的实现进行详细解析,并展示如何通过切面编程来动态赋值权限参数。
首先,我们需要定义一个权限控制的切面类,以便能够在特定方法执行前后进行逻辑处理。
@Aspect
@Component
public class AccessControlAspect{……
}
- @Aspect:声明一个切面类。
- @Component:将该切面类纳入 Spring 容器管理。
接下来,使用 @Pointcut 注解定义一个切入点,匹配所有被 @EmployeeAccessControl 注解的方法。
@Pointcut("@annotation(EmployeeAccessControl)")
public void AccessControl(){}
- @Pointcut:定义一个命名的切入点,便于在通知中引用。
- @annotation(EmployeeAccessControl):表示匹配所有带有 @EmployeeAccessControl 注解的方法。
然后,创建自定义注解EmployeeAccessControl,用来标记需要使用权限控制的方法。
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EmployeeAccessControl {String description() default "";
}
在方法执行前,我们通过 @Before 注解实现权限参数赋值的逻辑。
@Before("AccessControl()")
public void doBefore(JoinPoint joinPoint) {// 这里使用了动态代理,后续有时间了出篇博客详细来讲Object[] args = joinPoint.getArgs();PageParam pageParam = null;for (Object arg : args) {if (arg instanceof PageParam) {pageParam = (PageParam) arg;}}// 实体转化的逻辑忽略// 权限控制的参数赋值逻辑也忽略,自行补充。
}
最后,为了使切面生效,只需要在需要权限控制的方法前加上 @EmployeeAccessControl 注解即可。
@EmployeeAccessControl(description = "获取员工列表")
public List<Employee> getEmployeeList(PageParam pageParam) {// 方法逻辑,pageParam 中的权限参数已被赋值
}
切点(PointCut)和切面(Aspect)
最后,我们来一起回顾下切点和切面的这两个知识点。
在 Java 中,切点和切面是 AOP(面向切面编程)的两个关键概念。切点(Pointcut)定义了 “何时” 和 “何处” 触发切面(Aspect),而切面则包含了切点的具体逻辑实现,比如登录、事务管理等。简单来说,切面是启用横切关注点的具体实现,而切点是定义在哪些切入点进行横切的规范。