美文网首页
使用SpringAOP+自定义注解拦截请求

使用SpringAOP+自定义注解拦截请求

作者: 席坤 | 来源:发表于2019-10-12 16:27 被阅读0次

项目中需要对部分接口进行是否登录验证

主要通过自定义注解,使用SpringAOP的环绕通知拦截请求,判断该方法是否有自定义注解,然后判断该用户是否有该权限。这里做的比较简单,只有两个权限:一个普通用户、一个管理员。

添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

相关注解

@Before 前置通知,在方法执行之前执行

@After 后置通知,在方法执行之后执行

@AfterRuturning 返回通知,在方法返回结果之后执行

AfterThrowing 异常通知,在方法抛出异常之后执行

Around 环绕通知,围绕着方法执行

PointCut 表达式详解

execution:方法的返回值类型 包路径(可省略) 方法的名称(参数) 异常类型(可省略) )

  • 方法类型包含Public,Protected等,可省略

  • 方法返回值类型,*可以包含所有的返回值类型

  • 包路径,如“com.demo…*”,表示"com.demo"包以及该包之下子包的所有类型

  • 方法名称,如“add”,表示所有以add开头的方法,参数:()表示任意一个参数,(…)表示所有参数

  • 异常类型,如execution(* *(…) throws Exception)”匹配所有抛出Exception的方法

within:是用来指定类型的,指定类型中的所有方法将被拦截是用来指定类型的,指定类型中的所有方法将被拦截

  • within(com.demo.service.impl.UserServiceImpl) 匹配UserServiceImpl类对应对象的所有方法调用,并且只能是UserServiceImpl对象,不能是它的子对象

  • within(com.demo…*)匹配com.demo包及其子包下面的所有类的所有方法的外部调用。

this:SpringAOP是基于代理的,this就代表代理对象,语法是this(type),当生成的代理对象可以转化为type指定的类型时表示匹配

  • this(com.demo.service.IUserService)匹配生成的代理对象是IUserService类型的所有方法的外部调用

target:SpringAOP是基于代理的,target表示被代理的目标对象,当被代理的目标对象可以转换为指定的类型时则表示匹配。

  • target(com.demo.service.IUserService) 匹配所有被代理的目标对象能够转化成IuserService类型的所有方法的外部调用。

args:args用来匹配方法参数

  • args() 匹配不带参数的方法
  • args(java.lang.String) 匹配方法参数是String类型的
  • args(…) 带任意参数的方法
  • args(java.lang.String,…) 匹配第一个参数是String类型的,其他参数任意。最后一个参数是String的同理。

@within 和 @target:带有相应标注的所有类的任意方法,比如@Transactional

@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)

@annotation:带有相应标注的任意方法,比如@Transactional

@annotation(org.springframework.transaction.annotation.Transactional)
  • @within和@target针对类的注解,@annotation针对方法的注解

@args:参数带有相应标注的任意方法,比如@Transactiona

@args(org.springframework.transaction.annotation.Transactional)

PointCut使用

 /**
  * 拦截controller下的所有方法
  */
  @Pointcut("execution(public * com.mmall.controller..*.*(..))")
  public void loginRequired() {

   }

自定义注解及解析

  • 因为分客户端和管理端 定义不同注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LoginRequired {
    /**
     * 是否需要登录
     * @return
     */
    boolean loginRequired() default true;
}
  • 管理端
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AdminLoginRequired {
    /**
     * 是否需要登录
     * @return
     */
    boolean loginRequired() default true;
}
  • 解析类:通过AOP的环绕通知获取方法上的注解,判断是否有Permission注解,返回注解的值。
/**
     * 判断一个方法普通用户是否需要登录
     *
     * @param method 方法
     * @return
     */
    private boolean isLoginRequired(Method method) {
        boolean result = false;
        if (method.isAnnotationPresent(LoginRequired.class)) {
            result = method.getAnnotation(LoginRequired.class).loginRequired();
        }
        return result;
    }
/**
     * 判断一个方法管理员用户是否需要登录
     *
     * @param method 方法
     * @return
     */
    private boolean adminIsLoginRequired(Method method) {
        boolean result = false;
        if (method.isAnnotationPresent(AdminLoginRequired.class)) {
            result = method.getAnnotation(AdminLoginRequired.class).loginRequired();
        }
        return result;
    }
  • 判断用户是否登录
private Map<String, Object> isLogin(HttpServletRequest request) {
        String token = request.getHeader("token");
        //默认没登录普通管理员
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("role", 0);
        map.put("isLogin", false);
        if (null == token || "" == token) {
            return map;
        }
        String res = redisUtil.get(token) + "";
        User user = JSONObject.parseObject(res, User.class);
        if (user != null) {
            map.put("isLogin", true);
            if (user.getRole() == 1) {
                map.put("role", 1);
            }
        }
        return map;
    }

SpringAOP环绕通知

/**
     * 拦截器具体实现
     *
     * @param
     * @return
     */
    @Around("loginRequired()") //指定拦截器规则;
    @ResponseBody
    public Object Interceptor(ProceedingJoinPoint pjp) throws Throwable {

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod(); //获取被拦截的方法
        String methodName = method.getName(); //获取被拦截的方法名

        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();

        //判断该方法上没有权限注解,没用直接调用目标方法
        if (isLoginRequired(method) == true || adminIsLoginRequired(method) == true) {
            boolean loginResult = (boolean) isLogin(request).get("isLogin");

            if (loginResult == false) {
                return ServerResponse.createByErrorCodeMessage(100, "用户未登录");
            }else {
                if ((Integer) isLogin(request).get("role") == 0 && adminIsLoginRequired(method) == true) {
                    return ServerResponse.createByErrorCodeMessage(100, "当前用户不是管理员");
                }
            }
        }
        return pjp.proceed();
    }

使用

  • 普通用户添加LoginRequired注解
    @PostMapping("/updateInfo")
    @LoginRequired
    public ServerResponse<String> updateInfo(@RequestBody User user, HttpSession session) throws MmallException {
        ServerResponse response = userService.updateInfo(user);
        //更新成功刷新缓存
        if (response.isSuccess()) {
            session.setAttribute(Const.CURRENT_USER, response.getData());
        } else {
            return ServerResponse.createBySuccessMessage("更新失败");
        }
        return ServerResponse.createBySuccess();
    }
  • 管理员添加AdminLoginRequired
    @PostMapping("/categorySave")
    @AdminLoginRequired
    public ServerResponse<String> categorySave(@RequestBody Category category) {

        return categoryService.categorySave(category);
    }

完整代码见

相关文章

网友评论

      本文标题:使用SpringAOP+自定义注解拦截请求

      本文链接:https://www.haomeiwen.com/subject/vmaqmctx.html