前面实现了前端操作按钮,权限的控制。如何在后端实现权限控制,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);
}