使用Spring AOP做接口权限校验和日志记录

文章目录

  • 一、AOP 介绍
    • 1.1 AOP 应用场景
    • 1.2 AOP 中的注解
  • 二、权限校验
    • 2.1 定义权限注解
    • 2.2 定义切面类
    • 2.3 权限验证服务
    • 2.4 织入切点
    • 2.5 测试
  • 三、日志记录
    • 3.1 日志切面类
    • 3.2 异常统一处理
  • 四、AOP 底层原理
    • 4.1 todo

一、AOP 介绍

AOP: 翻译为面向切面编程(Aspect Oriented Programming),它是一种编程思想,是面向对象编程(OOP)的一种补充。它的目的是在不修改源代码的情况下给程序动态添加额外功能。

1.1 AOP 应用场景

AOP 的使用场景一般有:数据源切换、事务管理、权限控制、日志记录等。根据它的名字我们不难理解,它的实现很像是将我们要实现的代码切入业务逻辑中。

它有以下特点:

  1. 侵入性小,几乎可以不改动原先代码的情况下把新的功能加入到业务中。
  2. 实现方便,使用几个注解就可以实现,使系统更容易扩展。
  3. 更好的复用代码,比如事务日志打印,简单逻辑适合所有情况。

1.2 AOP 中的注解

@Aspect:切面,表示一个横切进业务的一个对象,一般定义为切面类,它里面包含切入点和通知。
@Pointcut:切入点, 表示需要切入的位置。比如某些类或者某些方法,也就是先定一个范围。
@Before:前置通知,切入点的方法体执行之前执行。
@Around:环绕通知,环绕切入点执行也就是把切入点包裹起来执行。
@After:后置通知,在切入点正常运行结束后执行。
@AfterReturning:后置通知,在切入点正常运行结束后执行,异常则不执行。
@AfterThrowing:后置通知,在切入点运行异常时执行。

二、权限校验

需求介绍:开发一个接口用于根据学生 id 获取学生身份证号,接口上需要做权限校验,只有系统管理员或者是机构管理员组织类型的账号才能执行此接口,其他组织类别及普通成员执行此接口,系统提示:没有权限。

2.1 定义权限注解

/**
 * 权限要求
 * 此注解用于标注于接口方法上, 根据属性group和role确定接口准入要求的组织和角色,
 * 标注此注解的方法会被切面{@link com.test.cloud.ig.vision.data.aspect.AuthorityAspect}拦截
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Auth {
    /**
     * 需要满足的组织类型
     * 默认值:{@link GroupType#SYSTEM}
     *
     * @return
     */
    GroupType[] group() default GroupType.SYSTEM;

    /**
     * 需要满足的角色类型
     * 默认值:{@link RoleType#ADMIN}
     *
     * @return
     */
    RoleType[] role() default RoleType.ADMIN;
}

2.2 定义切面类

@Aspect
@Order(1)
@Component
public class AuthorityAspect {

    @Autowired
    AuthorityService authorityService;

    @Pointcut(value = "@annotation(com.coe.cloud.ig.vision.data.annotation.Auth)")
    public void auth() {
    }

    @Before("auth()&&@annotation(auth)")
    public void before(Auth auth) {
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        
        // 从请求头中获取账号id
        String accountId = request.getHeader("accountId");
        
        // 校验权限
        authorityService.checkGroupAuthority(Integer.valueOf(accountId), auth.group(), auth.role());
    }
}

2.3 权限验证服务

@Service
public class AuthorityService {

    @Autowired
    AccountService accountService;
    /**
     * 判断账号是否有对应的组织操作权限
     *
     * @param accountId
     * @param groups    满足条件的组织级别
     * @param roles     满足条件的角色
     */
    public void checkGroupAuthority(Integer accountId, GroupType[] groups, RoleType[] roles) {
        // 根据账号ID获取账号信息
        TAccount account = accountService.findById(accountId);

        // 判断账号是否能操作此组织级别
        List<GroupType> controlGroups = GroupUtil.getControlGroups(GroupType.getByCode(account.getBizType()));
        controlGroups.retainAll(Arrays.asList(groups));
        AssertUtil.isFalse(controlGroups.isEmpty(), ResultCodes.NO_AUTHORITY);

        // 判断账号是否满足角色要求
        RoleType role = RoleType.getByCode(account.getRole());
        AssertUtil.isTrue(Arrays.asList(roles).contains(role), ResultCodes.NO_AUTHORITY);
    }
}    

2.4 织入切点

/**
 * 学生接口
 *
 * @author: huangBX
 * @date: 2023/5/24 15:16
 * @description:
 * @version: 1.0
 */
@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    StudentService studentService;
    
    @Autowired
    AccountService accountService;
    
    @Autowired
    AuthorityService authorityService;

    /**
     * 通过学生Id查询身份证号
     *
     * @param accountId
     * @param studentId
     * @return
     */
    @GetMapping ("/selectByStudentId")
    @Auth(group = {GroupType.SYSTEM, GroupType.ORGAN}, role = {RoleType.ADMIN})
    public Result<String> idCard(@RequestHeader("accountId") Integer accountId, @RequestParam("studentId") Integer studentId) {
        TAccount account = accountService.findById(accountId);
        AssertUtil.isNotNull(account, ResultCodes.ACCOUNT_NOT_FOUND);

        //校验是否有该学校的数据权限
        authorityService.checkDataAuthority(accountId, account.getBizId(), GroupType.ORGAN);

        TStudent student = studentService.findById(studentId);
        AssertUtil.isNotNull(student, ResultCodes.STUDENT_NOT_FOUND);
        return Result.success(student.getIdCard());
    }
}   

2.5 测试

账号信息表

在这里插入图片描述
role 角色字段若为 MEMBER 访问接口则提示没有权限。
使用Spring AOP做接口权限校验和日志记录_第1张图片

将 MEMBER 改为 ADMIN,重新发送请求,能够返回学生身份证号信息。

使用Spring AOP做接口权限校验和日志记录_第2张图片

三、日志记录

3.1 日志切面类

todo

3.2 异常统一处理

todo

四、AOP 底层原理

4.1 todo

你可能感兴趣的:(SpringBoot,框架,Spring,框架,MySQL,数据库,spring,java,数据库)