使用aop去实现token校验

前言

昨天去面试,被问懵的一个面试题,面试官看了一下简历,轻笑了一声原来你是用拦截器做的token校验啊,那么改用aop你怎么去做校验。我当时脑袋一篇空白。下面就写个小demo

AOP通知

先回顾一下aop的通知,aop通知有五种分别如下

  • 前置通知:方法执行前通知
  • 后置通知:方法执行后通知
  • 环绕通知:方法执行中通知
  • 异常抛出通知:方法抛出异常
  • 引介通知:类中增加新的方法属性

我们需要校验用户进行拦截oken,那么在执行到这个方法的时候拦截到,就需要用到环绕通知

导入Maven依赖

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

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

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.21</version>
        </dependency>

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

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

实际代码

我们先写一个登录类,使用hutool工具类去创建Token,中间加一些验证和设置过期时间

    @PostMapping("/login")
    public String userLogin(@RequestBody Map<String, String> map) {
        // 获取账号和密码
        String username = map.get("username");
        String password = map.get("password");
        // 进行校验
        if (StringUtils.isNotBlank(username) && StringUtils.isBlank(password)) {
            return "username || password error";
        }
        if (!username.equals("yefeng") && password.equals("123456")) {
            return "username || password error";
        }
        // 获取现在时间
        DateTime now = DateTime.now();
        // 过期时间为10分钟
        DateTime newTime = now.offsetNew(DateField.MINUTE, 10);

        Map<String, Object> payload = new HashMap();
        //签发时间
        payload.put(JWTPayload.ISSUED_AT, now);
        //过期时间
        payload.put(JWTPayload.EXPIRES_AT, newTime);
        //生效时间
        payload.put(JWTPayload.NOT_BEFORE, now);
        //载荷
        payload.put("username", username);
        payload.put("password", password);
        // 加密
        String key = "yefeng";
        String token = JWTUtil.createToken(payload, key.getBytes());
        return token;
    }

测试接口,发现Token生成成功,接下来我使用AOP去进行验证
image

首先我们创建一个TokenVerificationAop类, 并设置一个切入点和放行路径

@Aspect
@Component
@Slf4j
public class TokenVerificationAop {
    /**
     * 登录路径 ->放行
     */
    private static final String LOGIN = "login";

    /**
     * @Pointcut 声明切入点表达式。
     * 注意此处扫描的是你的Controller层接口位置
     */
    @Pointcut("execution(public * com.yefeng.controller..*..*(..))")
    public void pointcut() {
    }
}

设置好切入点之后,ProceedingJoinPoint是JoinPoint子接口,获取请求头里面的Authorization,判断请求URI是否包含login,如果包含就执行业务逻辑并且放行

    /**
     * @Around 环绕通知
     * @Around("pointcut()") 可以理解为对这个方法进行环绕通知
     * ProceedingJoinPoint 参数 用于环绕通知,
     * 使用proceed()方法来执行目标方法,可以理解为 前置通知结束 开始执行使用该注解的方法。
     */
    @Around("pointcut()")
    public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // assert 是判断然后报异常的, 符合条件继续往下进行
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader("Authorization");
        if (request.getRequestURI().contains(LOGIN)) {
            return joinPoint.proceed();
        }
        return null;
    }

如果不是登录接口那么我们需要进行Token校验具体代码如下

  @Around("pointcut()")
    public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        .....
        if (StringUtils.isNotBlank(token)) {
            try {
                String key = "yefeng";
                JWT jwt = JWTUtil.parseToken(token);
                boolean verify = jwt.setKey(key.getBytes()).verify();
                System.out.println(verify);
                boolean verifyTime = jwt.validate(0);
                System.out.println(verifyTime);
            } catch (Exception e) {
                // 解析jwt令牌出错, 说明令牌过期或者伪造等不合法情况出现 401
                log.info("AuthorizeFilter  解析jwt令牌出错", e);
                //此处可以返回自定义 Result结果对象 转成Json格式
                throw new Exception("解析jwt令牌出错");
            }
        } else {
            log.info("AuthorizeFilter  登录令牌不存在");
            throw new Exception("登录令牌不存在");
        }
        //执行业务逻辑,放行
        return joinPoint.proceed();
    }

完整代码如下

/**
 * 使用aop进行token校验
 * @author yefeng
 */
@Aspect
@Component
@Slf4j
public class TokenVerificationAop {
    /**
     * 登录路径 ->放行
     */
    private static final String LOGIN = "login";

    /**
     * @Pointcut 声明切入点表达式。
     * 注意此处扫描的是你的Controller层接口位置
     */
    @Pointcut("execution(public * com.yefeng.controller..*..*(..))")
    public void pointcut() {
    }

    /**
     * @Around 环绕通知
     * @Around("pointcut()") 可以理解为对这个方法进行环绕通知
     * ProceedingJoinPoint 参数 用于环绕通知,
     * 使用proceed()方法来执行目标方法,可以理解为 前置通知结束 开始执行使用该注解的方法。
     */
    @Around("pointcut()")
    public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // assert 是判断然后报异常的, 符合条件继续往下进行
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader("Authorization");
        if (request.getRequestURI().contains(LOGIN)) {
            //执行业务逻辑,放行
            return joinPoint.proceed();
        }
        if (StringUtils.isNotBlank(token)) {
            try {
                String key = "yefeng";
                JWT jwt = JWTUtil.parseToken(token);
                boolean verify = jwt.setKey(key.getBytes()).verify();
                System.out.println(verify);
                boolean verifyTime = jwt.validate(0);
                System.out.println(verifyTime);
                if (!verify) {
                    return "token 有误";
                }
            } catch (Exception e) {
                // 解析jwt令牌出错, 说明令牌过期或者伪造等不合法情况出现 401
                log.info("AuthorizeFilter  解析jwt令牌出错", e);
                //此处可以返回自定义 Result结果对象 转成Json格式
                throw new Exception("解析jwt令牌出错");
            }
        } else {
            log.info("AuthorizeFilter  登录令牌不存在");
            throw new Exception("登录令牌不存在");
        }
        //执行业务逻辑,放行
        return joinPoint.proceed();
    }
}

测试

写一个测试接口

    @GetMapping("/home")
    public String home() {
        return "token";
    }

我们随便写一个Token,发送请求,测试结果如下
image

那么我们弄一个有效Token,测试结果如下,说明我的aop进行token校验成功
image

posted @ 2023-10-25 16:06  ゐ叶う枫ゆ  阅读(161)  评论(0编辑  收藏  举报