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统一角色权限校验的资料请关注脚本之家其它相关文章!
