Spring boot通过AOP防止API重复请求代码实例
这篇文章主要介绍了Springboot通过AOP防止API重复请求代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
实现思路
基于SpringBoot2.x
自定义注解,用来标记是哪些API是需要监控是否重复请求
通过SpringAOP来切入到Controller层,进行监控
检验重复请求的Key:Token+ServletPath+SHA1RequestParas
- Token:用户登录时,生成的Token
- ServletPath:请求的Path
- SHA1RequestParas:将请求参数使用SHA-1散列算法加密
使用以上三个参数拼接的Key作为去判断是否重复请求
由于项目是基于集群的,使用Redis存储Key,而且redis的特性,key可以设定在规定时间内自动删除。这里的这个规定时间,就是api在规定时间内不能重复提交。
自定义注解(注解作用于Controller层的API)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public@interfaceNoRepeatSubmission{
}
切面逻辑
importcom.gotrade.apirepeatrequest.annotation.NoRepeatSubmission;
importcom.gotrade.apirepeatrequest.common.JacksonSerializer;
importcom.gotrade.apirepeatrequest.model.Result;
importlombok.extern.slf4j.Slf4j;
importorg.aspectj.lang.ProceedingJoinPoint;
importorg.aspectj.lang.annotation.Around;
importorg.aspectj.lang.annotation.Aspect;
importorg.aspectj.lang.reflect.MethodSignature;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.data.redis.core.RedisTemplate;
importorg.springframework.data.redis.core.ValueOperations;
importorg.springframework.stereotype.Component;
importorg.springframework.web.context.request.RequestContextHolder;
importorg.springframework.web.context.request.ServletRequestAttributes;
importjavax.servlet.http.HttpServletRequest;
importjava.nio.charset.StandardCharsets;
importjava.security.MessageDigest;
importjava.security.NoSuchAlgorithmException;
importjava.util.Objects;
importjava.util.concurrent.ConcurrentHashMap;
importjava.util.concurrent.TimeUnit;
@Slf4j
@Aspect
@Component
publicclassNoRepeatSubmissionAspect{
@Autowired
RedisTemplateredisTemplate;
/**
*环绕通知
*@parampjp
*@paramars
*@return
*/
@Around("execution(public*com.gotrade.apirepeatrequest.controller..*.*(..))&&@annotation(ars)")
publicObjectdoAround(ProceedingJoinPointpjp,NoRepeatSubmissionars){
ValueOperationsopsForValue=redisTemplate.opsForValue();
try{
if(ars==null){
returnpjp.proceed();
}
HttpServletRequestrequest=((ServletRequestAttributes)Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
Stringtoken=request.getHeader("Token");
if(!checkToken(token)){
returnResult.failure("Token无效");
}
StringservletPath=request.getServletPath();
StringjsonString=this.getRequestParasJSONString(pjp);
Stringsha1=this.generateSHA1(jsonString);
//key=token+servletpath
Stringkey=token+"-"+servletPath+"-"+sha1;
log.info("\n{\n\tServletPath:{}\n\tToken:{}\n\tJsonString:{}\n\tSHA-1:{}\n\tResultKey:{}\n}",servletPath,token,jsonString,sha1,key);
//如果Redis中有这个key,则url视为重复请求
if(opsForValue.get(key)==null){
Objecto=pjp.proceed();
opsForValue.set(key,String.valueOf(0),3,TimeUnit.SECONDS);
returno;
}else{
returnResult.failure("请勿重复请求");
}
}catch(Throwablee){
e.printStackTrace();
returnResult.failure("验证重复请求时出现未知异常");
}
}
/**
*获取请求参数
*@parampjp
*@return
*/
privateStringgetRequestParasJSONString(ProceedingJoinPointpjp){
String[]parameterNames=((MethodSignature)pjp.getSignature()).getParameterNames();
ConcurrentHashMapargs=null;
if(Objects.nonNull(parameterNames)){
args=newConcurrentHashMap<>(parameterNames.length);
for(inti=0;i>>4&0xf];
buf[k++]=hexDigits[byte0&0xf];
}
returnnewString(buf);
}catch(NoSuchAlgorithmExceptione){
e.printStackTrace();
}
returnnull;
}
}
切面主要逻辑代码,就是获取request中相关的信息,然后再拼接成一个key;判断在redis是否存在,不存在就添加并设置规定时间后自动移除,存在就是重复请求。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。