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){ ValueOperations opsForValue=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(); ConcurrentHashMap args=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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。