本文有点长,请耐心看完,重点都会有注释。
该方法采用元注解+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);
}
}