背景
最近在做一个小工具的权限控制系统,由于是第一次做这方面的工作,而且对权限的控制会非常的详细,比如,需要细致到对每一个对象的控制。在前期有一些调研。
想法
我大概实现的思路和设计
- 构建实体类,实现相应的数据存储化,用户可以设置相应的权限策略。权限设置,换一个说法就是主谓宾–“谁” “能看/不能看” “什么东西”。将这个权限设置抽象为对象,并进行存储。
- 根据实体类解析用户保存的策略,使之生效。
说起来简单,做起来难。第一点设置权限,其实就是非常简单的单表查询维护。难点在于第二点,如何能让这个策略生效。按照我目前接触和掌握到的技术手段,主要的解决方式有下面几种:
- 写一个intercepor,拦截器。但是这个方式,只能处理请求的控制器和方法,控制不了具体的参数。这一点比较适合用来做整体的系统权限拦截,拦截接口。拦截控制的粒度比较粗糙。
- 在每一个业务逻辑中,分别添加拦截的逻辑,即每一个返回对象的接口,都需要查询和判断权限表。优点:思路简单。缺点:可维护性差,写完第一次之后,再维护就是噩梦。
- 使用Aspect,可以自定义切入的点,甚至能细化到拦截什么包什么类的什么方法等等。Perfectly,就是他了。
除了上面的Interceptor和Aspect,还有filter。这三者实际上都是对Aop的具体实现,都可以实现权限检查,日志记录等等的功能。都是对业务逻辑的提取,不同的是,使用的范围 规范、和深度不同。
实现
由于包含工作的涉密内容,真实实现的代码就不在此展示。而网上的Demo已经相当丰富。
其中实现权限控制的最简单的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) {
// 这个地方就能够对返回值直接进行处理
}
}
- 定义切点pointCut(),这个地方,就是定义一个我们需要切入的点,我们要进行拦截处理的地方。
- 对这个切点有很多处理的方式,实现真正的权限拦截控制逻辑
其他的点
- 对于用户的组织权限的控制,我们可以使用路径的方式来进行判断,类似于一个树,我们每一次,存储的时候,需要将用户所在的组织架构的路径进行存储。这样我们在进行权限判断的时候,就不想要每一次都从库中获取用户的组织架构信息了。
- 对于model的校验可以写注解来进行实现。这一点,以后会专门写一篇文章来进行分享
- Npe问题可谓是开发过程中的一生之敌。在循环迭代过程中,不可以一遍迭代,一边删除list中的值。
- 使用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);
}