Springboot整合通过aop实现权限拦截过滤

学习理论知识,概念需要理清:

  • Pointcut:切点,决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为execution方式和annotation方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
  • Advice:处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
  • Aspect:切面,即Pointcut和Advice。
  • Joint point:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
  • Weaving:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。

ios开发拦截SDK点击事件 aop拦截service_连接点

参阅图片,可以梳理:
Pointcut切入点,归纳定义相应的join point;
织入器(参照pointcut定义取得相应的join point)将对应此pointcut的advice,织入到pointcut指定的join point初;
Join point执行具体的业务操作。


扩展 :

ios开发拦截SDK点击事件 aop拦截service_连接点_02


代码实践

  1. 定义注释:
/**
 * @Target 此注解的作用目标,括号里METHOD的意思说明此注解只能加在方法上面
 * @Retention 注解的保留位置,括号里RUNTIME的意思说明注解可以存在于运行时,可以用于反射
 * @Documented 说明该注解将包含在javadoc中
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StaffAnnotation {
}
  1. 定义切面
/**
 * 此类为一个切面类,主要作用就是对接口的请求进行拦截
 * 拦截的方式,只需要在指定接口方法上面加上@StaffAnnotation注解即可
 */

@Aspect
@Component
public class AuthorityAspect {

    @Autowired
    IUserService ius;

    //使用org.slf4j.Logger,这是spring实现日志的方法
    private final static Logger logger = LoggerFactory.getLogger(AuthorityAspect.class);

    /**
     * @Pointcut 注解指定一个切面,定义需要拦截的东西
     * 表示在执行被@MonitorRequest注解修饰的方法之前 会执行doBefore()方法
     */
    @Pointcut(value = "@annotation(com.sjy.it.user.annotation.StaffAnnotation)")
    public void checkStaffAuthority() {
    }

    /**
     *
     * @param joinPoint 连接点,就是被拦截点
     */
    @Before("checkStaffAuthority()")
    public void checkStaff(JoinPoint joinPoint) {
        //获取到请求的属性
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //获取到请求对象
        HttpServletRequest request = attributes.getRequest();

        //URL:根据请求对象拿到访问的地址
        logger.info("url=" + request.getRequestURL());
        //获取请求的方法,是Get还是Post请求
        logger.info("method=" + request.getMethod());
        //ip:获取到访问
        logger.info("ip=" + request.getRemoteAddr());
        //获取被拦截的类名和方法名
        logger.info("class=" + joinPoint.getSignature().getDeclaringTypeName() +
                "and method name=" + joinPoint.getSignature().getName());
        //参数
        logger.info("参数userId:" + Arrays.toString(joinPoint.getArgs()));

        Object[] joinPointArgs = joinPoint.getArgs();
        if(joinPointArgs==null){
            throw new ParameterException("用户信息拉取异常");
        }
        int userId = (int)joinPointArgs[0];
        logger.info("解析userId:"+userId);

        User user = ius.findUserById(userId);
        if(user==null){
            throw new AccountNotFoundException("账户不存在");
        }
        boolean hasPrivs = CheckPrivsCacheUtil.checkPrivs(user, PrivsEnmus.STAFF.getCode());
        if(hasPrivs){
            logger.info("鉴权成功,user:"+user.getUsername()+",权限:"+PrivsEnmus.STAFF.getName());
        } else {
            logger.error("鉴权失败,user:"+user.getUsername()+",权限:"+PrivsEnmus.STAFF.getName());
            throw new AuthorityException("权限不足");
        }

    }
}
  1. 定义Controller接口
@RequestMapping("/getStaffList")
    @StaffAnnotation
    public HttpResp getStaffList(@RequestParam(value = "userId") Integer userId) {
        List<User> staffList = ius.findAll();
        if (staffList != null && !staffList.isEmpty()) {
            return new HttpResp(0, "查询员工列表成功", staffList, new Date());
        } else {
            return new HttpResp(-1, "未查询到,请重试!", null, new Date());
        }
    }
  1. 发起请求:
getStaffList(){
    var _this = this;
    for (var state in _this.states) {
        _this.states[state] = 'display:none';
    }
    _this.states.showStaffList = 'display:block';
    axios.get('../api/staff/getStaffList?userId='+_this.userId)
        .then(function (response) {
            console.log(response.data);
            if (response.data.code == 0) {
                _this.staffTable = response.data.results;
            } else {
                if(response.data.msg!=null && response.data.msg!=undefined){
                    alert(response.data.msg);
                } else {
                    alert("拉取数据失败,请重试!")
                }
                _this.backToIndex();
            }
        })
        .catch(function (error) {
            console.log(error);
            _this.backToIndex();
        });
  1. 运行结果: