SpringBoot开发中的一些小细节(十三)使用AOP实现权限系统

SpringBoot开发中的一些小细节(十三)使用AOP实现权限系统

2020, May 12    

背景

最近在做一个小工具的权限控制系统,由于是第一次做这方面的工作,而且对权限的控制会非常的详细,比如,需要细致到对每一个对象的控制。在前期有一些调研。

想法

我大概实现的思路和设计

  • 构建实体类,实现相应的数据存储化,用户可以设置相应的权限策略。权限设置,换一个说法就是主谓宾–“谁” “能看/不能看” “什么东西”。将这个权限设置抽象为对象,并进行存储。
  • 根据实体类解析用户保存的策略,使之生效。

说起来简单,做起来难。第一点设置权限,其实就是非常简单的单表查询维护。难点在于第二点,如何能让这个策略生效。按照我目前接触和掌握到的技术手段,主要的解决方式有下面几种:

  • 写一个intercepor,拦截器。但是这个方式,只能处理请求的控制器和方法,控制不了具体的参数。这一点比较适合用来做整体的系统权限拦截,拦截接口。拦截控制的粒度比较粗糙。
  • 在每一个业务逻辑中,分别添加拦截的逻辑,即每一个返回对象的接口,都需要查询和判断权限表。优点:思路简单。缺点:可维护性差,写完第一次之后,再维护就是噩梦。
  • 使用Aspect,可以自定义切入的点,甚至能细化到拦截什么包什么类的什么方法等等。Perfectly,就是他了。

除了上面的Interceptor和Aspect,还有filter。这三者实际上都是对Aop的具体实现,都可以实现权限检查,日志记录等等的功能。都是对业务逻辑的提取,不同的是,使用的范围 规范、和深度不同。

执行顺序

Filter、Interceptor、Aop实现与区别

实现

由于包含工作的涉密内容,真实实现的代码就不在此展示。而网上的Demo已经相当丰富。

SpringBooot AOP的使用

其中实现权限控制的最简单的demo就是如下的代码

@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class AuthAspect {

   
    @Pointcut("execution(public * com.company.group.auth.service.AuthService.list(..))")
    public void pointCutForListObjective() {
    }
    
    /**
     * 对list的方法返回值进行拦截处理
     */
    @AfterReturning(pointcut = "pointCutForListObjective()", returning = "okrResponses")
    public void afterReturningListObjective(JoinPoint joinPoint, Object okrResponses) {
        // 这个地方就能够对返回值直接进行处理
    }

}


  1. 定义切点pointCut(),这个地方,就是定义一个我们需要切入的点,我们要进行拦截处理的地方。
  2. 对这个切点有很多处理的方式,实现真正的权限拦截控制逻辑

其他的点

  1. 对于用户的组织权限的控制,我们可以使用路径的方式来进行判断,类似于一个树,我们每一次,存储的时候,需要将用户所在的组织架构的路径进行存储。这样我们在进行权限判断的时候,就不想要每一次都从库中获取用户的组织架构信息了。
  2. 对于model的校验可以写注解来进行实现。这一点,以后会专门写一篇文章来进行分享
  3. Npe问题可谓是开发过程中的一生之敌。在循环迭代过程中,不可以一遍迭代,一边删除list中的值。
  4. 使用JPA的时候可以使用 pageable 来直接进行分页,不用再写mongoTemplate这种CustomRepo
      @Override
     public List<Department> listAuthDepartment(String departmentName) {
         if (Strings.isEmpty(departmentName)) {
             return Lists.newArrayList();
         }
         Sort sort = new Sort(Sort.Direction.DESC, "name");  
         Pageable pageable = PageRequest.of( 0, 20, sort); // (当前页, 每页记录数, 排序方式)
         return  departmentRepo.findAllByNameIsLike(departmentName, pageable);
     }