目录
1.介绍SpringAop
组成:
(1)切面(Aspect)
定义 AOP 是针对某个统一的功能的,这个功能就叫做一个切面,比如用户登录功能或方法的统计日志,他们就各是一个切面。切面是由切点和通知组成的
(2)连接点(Join Point)
所有可能触发 AOP(拦截方法的点)就称为连接点
(3)切点(Pointcut)
切点的作用就是提供一组规则来匹配连接点,给满足规则的连接点添加通知,总的来说就是,定义 AOP 拦截的规则的
切点相当于保存了众多连接点的一个集合(如果把切点看成一个表,而连接点就是表中一条一条的数据)
(4)通知(Advice)
切面的工作就是通知
通知:规定了 AOP 执行的时机和执行的方法
2.模块前景
由于做的是安全产品管理系统,所以对产品的每个模块和操作都有明确的权限限制,采用SpringAop和自定义注解实现是我能想到的最简单的实现方式,
下面展示代码实现:
1.准备一个切面
这里的切点和其他依赖自行创建,核心就是当前用户权限和接口权限若不匹配就抛出异常,
抛出的异常也是自定义异常,代码在后面
import com.shenyan.domain.annotation.ApiLimitedRole;
import com.shenyan.domain.response.vo.LoginUserRespVo;
import com.shenyan.domain.response.vo.RoleInfoRespVo;
import com.shenyan.common.exception.ConditionException;
import com.shenyan.common.exception.ConditionExceptionEnum;
import com.shenyan.service.RoleInfoService;
import com.shenyan.service.UserInfoService;
import com.shenyan.common.support.UserSupport;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@Order(1)
@Component
@Aspect
public class ApiLimitedRoleAspect {
@Autowired
private UserSupport mUserSupport;
@Autowired
private UserInfoService userInfoService;
@Autowired
private RoleInfoService mRoleInfoService;
@Pointcut("@annotation(com.shenyan.domain.annotation.ApiLimitedRole)")
public void check(){
}
@Before("check() && @annotation(apiLimitedRole)")
public void doBefore(JoinPoint joinPoint, ApiLimitedRole apiLimitedRole) {
UUID userId = mUserSupport.getCurrentUserId();
LoginUserRespVo loginUserRespVo = userInfoService.queryUserById(userId);
if (Objects.isNull(loginUserRespVo)) {
throw new ConditionException(ConditionExceptionEnum.AUTHENTICATION_ERROR);
}
Long roleId = loginUserRespVo.getRoleId();
RoleInfoRespVo roleInfoRespVo = mRoleInfoService.queryRole(roleId);
String roleCode = roleInfoRespVo.getRoleCode();
//获取当前接口需要的权限
String limitedRoleCodeList = apiLimitedRole.limitedRoleCodeList();
int i = limitedRoleCodeList.lastIndexOf("1");
//判断当前用户是否具有此权限
if (roleCode.charAt(i) != '1') {
throw new ConditionException(ConditionExceptionEnum.AUTHENTICATION_NOT_ENOUGH);
}
}
}
2.自定义注解类
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
@Component
public @interface ApiLimitedRole {
//权限列表
String limitedRoleCodeList() default "";
}
3.创建常量类
这个类包含30种权限
package com.shenyan.common.constant;
public interface Role {
/**
* 主程序看板
*/
public static final String MAIN_BOARD =
"1";
/**
* 视频预览
*/
public static final String VIDEO_PREVIEW =
"01";
/**
* 设备查询
*/
public static final String DEVICE_QUERY =
"001";
/**
* 设备新增
*/
public static final String DEVICE_ADD =
"0001";
/**
* 设备修改
*/
public static final String DEVICE_UPDATE =
"00001";
/**
* 设备删除
*/
public static final String DEVICE_DELETE =
"000001";
/**
* 任务管理
*/
public static final String TASK_QUERY =
"0000001";
/**
* 任务新增
*/
public static final String TASK_ADD =
"00000001";
/**
* 任务修改
*/
public static final String TASK_UPDATE =
"000000001";
/**
* 任务删除
*/
public static final String TASK_DELETE =
"0000000001";
/**
* 事件查询
*/
public static final String EVENT_QUERY =
"00000000001";
/**
* 系统管理
*/
public static final String SYSTEM_MANAGE =
"000000000001";
/**
* 用户查询
*/
public static final String USER_QUERY =
"0000000000001";
/**
* 用户新增
*/
public static final String USER_ADD =
"00000000000001";
/**
* 用户修改
*/
public static final String USER_UPDATE =
"000000000000001";
/**
* 用户删除
*/
public static final String USER_DELETE =
"0000000000000001";
/**
* 角色查询
*/
public static final String ROLE_QUERY =
"00000000000000001";
/**
* 角色新增
*/
public static final String ROLE_ADD =
"000000000000000001";
/**
* 角色修改
*/
public static final String ROLE_UPDATE =
"0000000000000000001";
/**
* 角色删除
*/
public static final String ROLE_DELETE =
"00000000000000000001";
/**
* 看板查询
*/
public static final String DASHBOARD_QUERY =
"000000000000000000001";
/**
* 看板新增
*/
public static final String DASHBOARD_ADD =
"0000000000000000000001";
/**
* 看板修改
*/
public static final String DASHBOARD_UPDATE =
"00000000000000000000001";
/**
* 看板删除
*/
public static final String DASHBOARD_DELETE =
"000000000000000000000001";
/**
* 学校查询
*/
public static final String SCHOOL_QUERY =
"0000000000000000000000001";
/**
* 学校新增
*/
public static final String SCHOOL_ADD =
"00000000000000000000000001";
/**
* 学校修改
*/
public static final String SCHOOL_UPDATE =
"000000000000000000000000001";
/**
* 学校删除
*/
public static final String SCHOOL_DELETE =
"0000000000000000000000000001";
/**
* 添加设备
*/
public static final String DEVICE_ADD1 =
"00000000000000000000000000001";
/**
* 系统日志
*/
public static final String SYSTEM_LOG =
"000000000000000000000000000001";
}
4.自定义异常(运行时异常)
public class ConditionException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String code;
public ConditionException(ConditionExceptionEnum conditionExceptionEnum){
super(conditionExceptionEnum.getDesc());
this.code = conditionExceptionEnum.getCode();
}
public ConditionException(String code, String name) {
super(name);
this.code = code;
}
public ConditionException(String name){
super(name);
code="500";
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
5.全局异常捕获
捕获自定义异常
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.shenyan.common.constant.Constants;
import com.shenyan.common.exception.ConditionException;
import com.shenyan.common.exception.ConditionExceptionEnum;
import com.shenyan.domain.JsonResponse;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ComonGlobalExceptionHandler {
@ExceptionHandler(value = ConditionException.class)
@ResponseBody
public JsonResponse<String> conditionException(ConditionException e){
return JsonResponse.fail(e.getCode(),e.getMessage());
}
}
6.Controller层使用
@PostMapping("/getAllDevice/{page}/{pageSize}")
@ApiLimitedRole(limitedRoleCodeList =Role.DEVICE_QUERY )
public JsonResponse<Object> selectAllDevice() {
return null;
}
整个流程就是
用户请求----》在controller层之前被Aop拦截验证是否符合------》不符合抛出异常------》全局异常捕获异常