Spring AOP 面向切面编程

128 阅读2分钟

前面实现了前端操作按钮,权限的控制。如何在后端实现权限控制,Spring AOP 在权限控制这块就起到了作用。

Spring AOP 是什么

抽离出固化流程,插入固化流程的一种框架。

  • 是一种约定流程的编程
  • 核心 约定。(AOP的核心)
  • 典型例子:数据库事务。包括打开数据库,设置属性,执行SQL语句(个性化操作,不是固定好的功能。)。没有异常提供事务,有异常回滚事务(固化流程)。最后关闭数据库连接。

除了执行sql语句,其他都是固定好的流程。

将个性化操作 织入(waving) 到固定化流程,这就是 Spring AOP 框架起到的作用。
最大的好处就是省去 很多执行固化操作的时间。

以权限控制为例

约定的流程:判断角色,权限,执行个性化的逻辑,抛出没有权限异常。。。 由此判断可以使用Spring AOP 框架。

术语 概念

  • join point: 对应被拦截的对象。
  • 切点 : 通过正则或指示器的规则来适配连接点。
  • 切面 : 定义切点,各类通知和引用的内容。切入点和通知的结合。
  • 通知(advice): 前置通知、后置通知、事后返回通知、异常通知
  • 织入(waving):为原有服务对象生成代理对象,将与切点匹配的连接点进行拦截,并将各类通知加入约定的流程。个性化操作。
  • 目标对象(target):被代理的对象。

实现接口权限控制

自定义注解
@Retention(RetentionPolicy.RUNTIME)
//@Target({ElementType.TYPE})
// 在方法上的权限
@Target(ElementType.METHOD)
@Documented
@Component
public @interface ApiLimited {
     String[] userRoleCode() default {};

}
定义切面
@Aspect
@Component
public class ControllerLimited {

    private static final Logger LOG = LoggerFactory.getLogger(ControllerLimited.class);
    // 获取用户对象
    @Resource
    private UserSupport userSupport;

    @Resource
    private UserRoleService userRoleService;
    // 定义切点
    @Pointcut("@annotation(nuc.zm.server.commons.ApiLimited)")
    public void check(){}

    // 定义通知 进入 API 时,先进行权限验证
    @Before("check() && @annotation(api)")
    public void before(JoinPoint joinPoint, ApiLimited api){
        // 获取当前用户id
        Long currentUserId = userSupport.getCurrentUserId();
        // 获取当前 用户对应的角色对象
        List<AuthRoleUserDto> roleList = userRoleService.getRolesByUserId(currentUserId);
        String[] userRoleCodes = api.userRoleCode();
        Set<String> annotationRoleSet = Arrays.stream(userRoleCodes).collect(Collectors.toSet());
        Set<String> userRoleSet = roleList.stream().map(AuthRoleUserDto::getRoleName).collect(Collectors.toSet());
        // 若包含 限制访问的角色 即 集合大小 > 0 则 限制该用户访问
        userRoleSet.retainAll(annotationRoleSet);
        if (userRoleSet.size() > 0) throw new ConditionException(ExceptionEnum.AUTH_LACK);
    }
}
controller层 限制用户0访问
@PostMapping("/ger-user-info")
@ApiLimited(userRoleCode = LimitedConstant.USER_LV0)
public ResponseVo<UserInfoDto> getUserInfo(){
    Long currentUserId = userSupport.getCurrentUserId();
    UserInfoDto userInfo = userService.getUserInfo(currentUserId);
    return ResponseVo.ok(userInfo);
}