SpringBoot通过AOP实现自定义注解验证Token

本文有点长,请耐心看完,重点都会有注释。
该方法采用元注解+AOP方式进行编写。

首先引入AOP(必要)依赖及JSON解析依赖(非必要)

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.67</version>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

创建一个注解

import java.lang.annotation.*;

/**
 * @author hxy
 * @Target 该注解声明可以使用的范围
 *  ElementType.METHOD 即为可在方法上使用
 * @Retention 用于描述注解的声明周期
 *  RetentionPolicy.RUNTIME 运行时
 *  RetentionPolicy.CLASS   编译时
 *  RetentionPolicy.RUNTIME 源码时
 *  注意 需用 @interface修饰。
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckToken {
    /**
     * 默认参数,也可置空
     * 或像以下方式添加默认值
     * String value() default "hxy";
     */
    String value();

}

创建一个切面类

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @author Administrator
 * @Aspect 声明是个切面的类(必要)
 * @Component 将该类交给spring容器管理(必要)
 * @Slf4j 日志输出
 */
@Aspect
@Component
@Slf4j
public class CheckTokenAspect {

    @Resource
    private CorpTokenMapper corpTokenMapper;

    /**
     * @Pointct 表示该方法可用于 com.tdkj.DataApi.annotation.CheckToken 这个注解
     * 还可以使用*号表示通配符
     */
    @Pointcut("@annotation(com.tdkj.DataApi.annotation.CheckToken)")
    public void pointcut() {
    }

    /**
     *  @Around 环绕通知
     *  @Around("pointcut()") 可以理解为对这个方法进行环绕通知
     *  ProceedingJoinPoint 参数 用于环绕通知,
     *  使用proceed()方法来执行目标方法,可以理解为 前置通知结束 开始执行使用该注解的方法。
     */
    @Around("pointcut()")
    public Object checkTokenAroundAspect(ProceedingJoinPoint joinPoint) throws Throwable {
        /**
         * 以下部分不重要,是我自己的业务逻辑 开始
         */
        //获取请求头
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //从请求头中获取需要的参数
        String token = request.getHeader("token");
        String secretKey = request.getHeader("secretKey");
        //检查参数是否存在及长度是否符合要求
        if (StringUtil.isEmpty(token)||StringUtil.isEmpty(secretKey)||token.length()!=8||secretKey.length()!=16){
            //返回的错误信息将会直接返回给用户。 可看本文最后一张图
            return new Response().code(HttpStatus.BAD_REQUEST).message("token/secretKey缺失或长度有误");
        }
        //使用mybatis-plus查询数据库
        QueryWrapper<CorpToken> wrapper = new QueryWrapper<>();
        wrapper.eq("t.corp_token", token).eq("t.corp_secret_key",secretKey);
        CorpTokenVO cTVO= corpTokenMapper.getCorpTokenOne(wrapper);
        //未找到对应token
        if (StringUtil.isEmpty(cTVO)){
            return new Response().code(HttpStatus.FORBIDDEN).message("token或secretKey错误");
        }
        //调用方法获取请求地址
        String ip = IpUtil.getIpAddr(request);
        /**
         * 以上部分不重要,是我自己的业务逻辑  结束
         */
        /**
         * 以下至本方法结束都很重要
         * 以下至本方法结束都很重要
         * 以下至本方法结束都很重要
         */
        //获取方法上的注解信息,并打印方法入参出参,这里调用了该类的其他方法
        Method method = this.getMethod(joinPoint);
        //通过method对象获取到使用该注解的方法。就可以获取到通过注解传过来的参数。
        CheckToken checkToken = method.getAnnotation(CheckToken.class);
        //获取注解上的参数
        String value = checkToken.value();
        /**
         *  获取使用该注解方法的参数列表
         *  这个和main方法的String[] args 一样就是个参数,
         *  区别在于类型不同
         */
        Object[] args = joinPoint.getArgs();
        /**
         * 将下标为0的参数转换成使用该注解的方法中对应参数的类型,
         * 就可以在方法执行前,获取到该注解执行后传给该方法的参数。
         * 这里传入了一个对象
         * 对象包含属性 1.corpId 2.corpName 3.Ip
         * 这个地方需要注意下。待会在调用的时候就会豁然开朗
         */
        CorpTokenVO newCorpToken = (CorpTokenVO) args[0];
        newCorpToken.setCorpId(cTVO.getCorpId());
        newCorpToken.setCorpName(cTVO.getCorpName());
        newCorpToken.setIp(ip);
        // 打印入参 就只是输出
        this.printParam(value, args);
        /**
         * 执行原有方法逻辑 相当于前置通知走完了,可以走原本方法的逻辑了。
         * 走完后获取方法返回的参数
         */
        Object result = joinPoint.proceed();
        // 打印出参 就只是输出
        this.printResult(value, result);
        return result;
    }

    /**
     * 获取调用的方法
     */
    private Method getMethod(ProceedingJoinPoint jp) throws Exception {
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        return jp.getTarget().getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
    }

    /**
     * 打印入参
     *
     * @param key  方法名称
     * @param args 参数
     */
    private void printParam(String key, Object[] args) {
        log.info("{}方法入参为:{}", key, JSONObject.toJSONString(args));
    }

    /**
     * 打印出参
     *
     * @param key    方法名称
     * @param result 方法返回值
     */
    private void printResult(String key, Object result) {
        log.info("{}方法出参为:{}", key, JSONObject.toJSONString(result));
    }
}

至此,完整的注解就写完了。
调用代码
在这里插入图片描述
postman测试
这里只传入了这两个参数,用于验证身份
在这里插入图片描述
日志输出
在这里插入图片描述
代码中return中返回的错误信息将会直接返回给用户。
废话:感觉跟 POST请求的方法用GET请求返回的方式差不多 Request method ‘GET’ not supported
在这里插入图片描述

如果有哪里看不明白可留言。

代码中使用的isEmpty是我重写后的。

import org.springframework.lang.Nullable;

/**
 * @author hxy
 * @version 1.0
 * @date 2020/10/16 15:21
 */
public class StringUtil {

    public static boolean isEmpty(@Nullable Object str) {
        return str == null || "".equals(str) || "null".equals(str);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值