文章目录
- 一、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 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 访问接口则提示没有权限。
将 MEMBER 改为 ADMIN,重新发送请求,能够返回学生身份证号信息。
三、日志记录
3.1 日志切面类
todo
3.2 异常统一处理
todo