SpringBoot使用AOP实现统一角色权限校验
作者:天罡gg
一、引入AOP starter
在tg-book-common中引入依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
对于spring boot的starter,我在之前的文章中已经反复说明过多次,不做赘述!
本项目中已经使用中的starter如下:
- spring-boot-starter-web
- spring-boot-starter-logging
- mybatis-spring-boot-starter
- pagehelper-spring-boot-starter
二、创建切面@Aspect + 定义切点@Pointcut
@Aspect
注解方式,它的概念像@Aspect、@Pointcut、@Before、@After、@Around等注解都是来自于 AspectJ,但是功能的实现是纯 Spring AOP 自己实现的,主要有两大核心
:
- 定义[切入点]:使用 @Pointcut 切点表达式,你可以理解成类似于正则表达式的强大东东。(例如本文的@annotation方式)
- 定义[切入时机] 和 [增强处理逻辑]:五种通知Advice注解 对[切入点]执行增强处理, 包括:@Before、@After、@AfterRunning、@AfterThrowing、@Around
以下使用@Aspect 定义一个切面类,使用@Pointcut定义一个切点,切点表达式使用@annotation方式,也就是注解的方式。
// @Aspect和@Component定义一个切面类,@Slf4j是之前讲过的日志注解 @Component @Aspect @Slf4j public class RoleAspect { // 核心一:定义切点(使用@annotation方式) @Pointcut(value = "@annotation( org.tg.book.common.annotation.Role)") public void pointCut() {} }
三、封装校验@Role角色权限的方法
本文的AOP是上文拦截器Interceptor的另一种实现方式,所以请将上文的AuthInterceptor中的如下代码注释:
然后把这段代码拿过来,封装成一个方法,放到RoleAspect
中如下:
/** * 将@Role与登录用户的角色对比,如果是管理员返回true **/ private boolean checkAdminRole(Role role) { // 校验角色 if (role != null) { // 走到这,说明方法上加了@Role boolean isAdmin = false; AuthContextInfo authInfo = AuthContextInfo.getAuthInfo(); for (int roleId : role.roleIds()) { if (authInfo.getRoleId().equals(roleId)) { isAdmin = true; break; } } if (!isAdmin) { log.info("[403]无权限, authInfo={}", authInfo); return false; } } return true; }
方法逻辑很简单:将@Role与登录用户的角色对比,如果是管理员返回true,否则返回false
四、AOP两种实现方式
4.1 前置通知@Before方式
因为角色权限校验代码,发生于【业务方法代码】之前,所以可以使用前置通知@Before方式,代码如下:
@Before("pointCut()") public void before(JoinPoint joinPoint) throws NoSuchMethodException { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Class<?> clazz = joinPoint.getTarget().getClass(); Method method = clazz.getMethod(signature.getName(), signature.getParameterTypes()); Role role = method.getAnnotation(Role.class); boolean isAdminRole = checkAdminRole(role); if (!isAdminRole) { throw new RuntimeException("无权限"); } }
核心逻辑是获得@Role注解,然后进行校验,如果非管理员,则
抛出异常
。这里实现的比较简单,当后面我们实现了【全局异常处理】以后,这里就可以换成自定义的异常类,交给【全局异常处理】统一处理!
4.2 环绕通知@Around方式
如果不抛出异常的话,如何处理?
可以使用@Around方式,环绕通知@Around可以控制在【业务方法代码】之前校验,并且可以返回结果
,所以我们就不需要抛出异常了!
@Around("pointCut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Class<?> clazz = joinPoint.getTarget().getClass(); Method method = clazz.getMethod(signature.getName(), signature.getParameterTypes()); Role role = method.getAnnotation(Role.class); boolean isAdminRole = checkAdminRole(role); if (!isAdminRole) { return TgResult.fail("403", "无权限"); } return joinPoint.proceed(); }
获取Role 之前的代码都是一模一样的,区别就是这里没有抛出异常,而是返回统一结果TgResult,这也正是封装统一返回结果的好处之一!!!
特别注意: before和around是两种实现方式,所以不必在意从joinPoint得到role的重复代码,因为最终只会写一份代码,对于before和around我更建议使用around的方式!
以上就是SpringBoot使用AOP实现统一角色权限校验的详细内容,更多关于SpringBoot AOP统一角色权限校验的资料请关注脚本之家其它相关文章!