Springboot怎么利用Redis实现接口幂等性拦截(redis,springboot,开发技术)

时间:2024-04-29 22:34:51 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

正文

Springboot怎么利用Redis实现接口幂等性拦截

自定义注解 怎么玩的 :

①标记哪个接口需要进行幂等性拦截

②每个接口可以要求幂等性范围时间不一样,举例:可以2秒内,可以3秒内,时间自己传

③ 一旦触发了,提示语可以不同 ,举例:VIP的接口,普通用户的接口,提示语不一样(开玩笑)

效果:

Springboot怎么利用Redis实现接口幂等性拦截

Springboot怎么利用Redis实现接口幂等性拦截

实战开始

核心三件套

注解、拦截器、拦截器配置

① RepeatDaMie.java

importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/***@Author:JCccc*@Date:2022-6-139:04*@Description:自定义注解,防止重复提交*/@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public@interfaceRepeatDaMie{/***时间ms限制*/publicintsecond()default1;/***提示消息*/publicStringdescribe()default"重复提交了,兄弟";}

②ApiRepeatInterceptor.java

importcom.example.repeatdemo.annotation.RepeatDaMie;importcom.example.repeatdemo.util.ContextUtil;importcom.example.repeatdemo.util.Md5Encrypt;importcom.example.repeatdemo.util.RedisUtils;importcom.example.repeatdemo.wrapper.CustomHttpServletRequestWrapper;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.HandlerInterceptor;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.util.Objects;/***@Author:JCccc*@Date:2022-6-159:11*@Description:接口幂等性校验拦截器*/@ComponentpublicclassApiRepeatInterceptorimplementsHandlerInterceptor{privatefinalLoggerlog=LoggerFactory.getLogger(this.getClass());privatestaticfinalStringPOST="POST";privatestaticfinalStringGET="GET";@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{try{if(handlerinstanceofHandlerMethod){HandlerMethodhandlerMethod=(HandlerMethod)handler;//获取RepeatDaMie注解RepeatDaMierepeatDaMie=handlerMethod.getMethodAnnotation(RepeatDaMie.class);if(null==repeatDaMie){returntrue;}//限制的时间范围intseconds=repeatDaMie.second();//这个用户唯一标识,可以自己细微调整,是userId还是token还是sessionId还是不需要StringuserUniqueKey=request.getHeader("userUniqueKey");Stringmethod=request.getMethod();StringapiParams="";if(GET.equals(method)){log.info("GET请求来了");apiParams=newObjectMapper().writeValueAsString(request.getParameterMap());}elseif(POST.equals(method)){log.info("POST请求来了");CustomHttpServletRequestWrapperwrapper=(CustomHttpServletRequestWrapper)request;apiParams=wrapper.getBody();}log.info("当前参数是:{}",apiParams);//存储keyStringkeyRepeatDaMie=Md5Encrypt.md5(userUniqueKey+request.getServletPath()+apiParams);RedisUtilsredisUtils=ContextUtil.getBean(RedisUtils.class);if(Objects.nonNull(redisUtils.get(keyRepeatDaMie))){log.info("重复请求了,重复请求了,拦截了");returnData(response,repeatDaMie.describe());returnfalse;}else{redisUtils.setWithTime(keyRepeatDaMie,true,seconds);}}returntrue;}catch(Exceptione){log.warn("请求过于频繁请稍后再试");e.printStackTrace();}returntrue;}publicvoidreturnData(HttpServletResponseresponse,Stringmsg)throwsIOException{response.setCharacterEncoding("UTF-8");response.setContentType("application/json;charset=utf-8");ObjectMapperobjectMapper=newObjectMapper();//这里传提示语可以改成自己项目的返回数据封装的类response.getWriter().println(objectMapper.writeValueAsString(msg));return;}}

③ WebConfig.java

importorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;/***@Author:JCccc*@Date:2022-6-159:24*@Description:*/@ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newApiRepeatInterceptor()).addPathPatterns("/**");}}

工具类三件套

①ContextUtil.java

importorg.springframework.beans.BeansException;importorg.springframework.context.ApplicationContext;importorg.springframework.context.ApplicationContextAware;importorg.springframework.stereotype.Component;/***@Author:JCccc*@Date:2022-6-159:24*@Description:*/@ComponentpublicfinalclassContextUtilimplementsApplicationContextAware{protectedstaticApplicationContextapplicationContext;@OverridepublicvoidsetApplicationContext(ApplicationContextarg0)throwsBeansException{if(applicationContext==null){applicationContext=arg0;}}publicstaticObjectgetBean(Stringname){//name表示其他要注入的注解name名returnapplicationContext.getBean(name);}/***拿到ApplicationContext对象实例后就可以手动获取Bean的注入实例对象*/publicstatic<T>TgetBean(Class<T>clazz){returnapplicationContext.getBean(clazz);}}

②Md5Encrypt.java

importjava.io.UnsupportedEncodingException;importjava.security.MessageDigest;importjava.security.NoSuchAlgorithmException;/***@Author:JCccc*@CreateTime:2018-10-30*@Description:*/publicclassMd5Encrypt{privatestaticfinalchar[]DIGITS={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};/***对字符串进行MD5加密**@paramtext明文*@return密文*/publicstaticStringmd5(Stringtext){MessageDigestmsgDigest=null;try{msgDigest=MessageDigest.getInstance("MD5");}catch(NoSuchAlgorithmExceptione){thrownewIllegalStateException("Systemdoesn'tsupportMD5algorithm.");}try{//注意该接口是按照指定编码形式签名msgDigest.update(text.getBytes("UTF-8"));}catch(UnsupportedEncodingExceptione){thrownewIllegalStateException("Systemdoesn'tsupportyourEncodingException.");}byte[]bytes=msgDigest.digest();Stringmd5Str=newString(encodeHex(bytes));returnmd5Str;}privatestaticchar[]encodeHex(byte[]data){intl=data.length;char[]out=newchar[l<<1];//twocharactersformthehexvalue.for(inti=0,j=0;i<l;i++){out[j++]=DIGITS[(0xF0&data[i])>>>4];out[j++]=DIGITS[0x0F&data[i]];}returnout;}}

③RedisUtils.java

importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.*;importorg.springframework.stereotype.Component;importjava.io.Serializable;importjava.util.List;importjava.util.Set;importjava.util.concurrent.TimeUnit;@ComponentpublicclassRedisUtils{@AutowiredprivateRedisTemplateredisTemplate;/***写入String型[键,值]**@paramkey*@paramvalue*@return*/publicbooleanset(finalStringkey,Objectvalue){booleanresult=false;try{ValueOperations<Serializable,Object>operations=redisTemplate.opsForValue();operations.set(key,value);result=true;}catch(Exceptione){e.printStackTrace();}returnresult;}/***写入String型,顺便带有过期时间[键,值]**@paramkey*@paramvalue*@return*/publicbooleansetWithTime(finalStringkey,Objectvalue,intseconds){booleanresult=false;try{ValueOperations<Serializable,Object>operations=redisTemplate.opsForValue();operations.set(key,value,seconds,TimeUnit.SECONDS);result=true;}catch(Exceptione){e.printStackTrace();}returnresult;}/***批量删除对应的value**@paramkeys*/publicvoidremove(finalString...keys){for(Stringkey:keys){remove(key);}}/***批量删除key**@parampattern*/publicvoidremovePattern(finalStringpattern){Set<Serializable>keys=redisTemplate.keys(pattern);if(keys.size()>0)redisTemplate.delete(keys);}/***删除对应的value**@paramkey*/publicvoidremove(finalStringkey){if(exists(key)){redisTemplate.delete(key);}}/***判断缓存中是否有对应的value**@paramkey*@return*/publicbooleanexists(finalStringkey){returnredisTemplate.hasKey(key);}/***读取缓存**@paramkey*@return*/publicObjectget(finalStringkey){Objectresult=null;ValueOperations<Serializable,Object>operations=redisTemplate.opsForValue();result=operations.get(key);returnresult;}/***哈希添加*hash一个键值(key->value)对集合**@paramkey*@paramhashKey*@paramvalue*/publicvoidhmSet(Stringkey,ObjecthashKey,Objectvalue){HashOperations<String,Object,Object>hash=redisTemplate.opsForHash();hash.put(key,hashKey,value);}/***Hash获取数据**@paramkey*@paramhashKey*@return*/publicObjecthmGet(Stringkey,ObjecthashKey){HashOperations<String,Object,Object>hash=redisTemplate.opsForHash();returnhash.get(key,hashKey);}/***列表添加*list:lpushkeyvalue1**@paramk*@paramv*/publicvoidlPush(Stringk,Objectv){ListOperations<String,Object>list=redisTemplate.opsForList();list.rightPush(k,v);}/***列表List获取*lrange:key010(读取的个数从0开始读取到下标为10的数据)**@paramk*@paraml*@paraml1*@return*/publicList<Object>lRange(Stringk,longl,longl1){ListOperations<String,Object>list=redisTemplate.opsForList();returnlist.range(k,l,l1);}/***Set集合添加**@paramkey*@paramvalue*/publicvoidadd(Stringkey,Objectvalue){SetOperations<String,Object>set=redisTemplate.opsForSet();set.add(key,value);}/***Set集合获取**@paramkey*@return*/publicSet<Object>setMembers(Stringkey){SetOperations<String,Object>set=redisTemplate.opsForSet();returnset.members(key);}/***Sortedset:有序集合添加**@paramkey*@paramvalue*@paramscoure*/publicvoidzAdd(Stringkey,Objectvalue,doublescoure){ZSetOperations<String,Object>zset=redisTemplate.opsForZSet();zset.add(key,value,scoure);}/***Sortedset:有序集合获取**@paramkey*@paramscoure*@paramscoure1*@return*/publicSet<Object>rangeByScore(Stringkey,doublescoure,doublescoure1){ZSetOperations<String,Object>zset=redisTemplate.opsForZSet();returnzset.rangeByScore(key,scoure,scoure1);}/***根据key获取Set中的所有值**@paramkey键*@return*/publicSet<Integer>sGet(Stringkey){try{returnredisTemplate.opsForSet().members(key);}catch(Exceptione){e.printStackTrace();returnnull;}}/***根据value从一个set中查询,是否存在**@paramkey键*@paramvalue值*@returntrue存在false不存在*/publicbooleansHasKey(Stringkey,Objectvalue){try{returnredisTemplate.opsForSet().isMember(key,value);}catch(Exceptione){e.printStackTrace();returnfalse;}}}

REDIS配置类

RedisConfig.java

importcom.fasterxml.jackson.annotation.JsonAutoDetect;importcom.fasterxml.jackson.annotation.PropertyAccessor;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.springframework.cache.CacheManager;importorg.springframework.cache.annotation.EnableCaching;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.data.redis.cache.RedisCacheConfiguration;importorg.springframework.data.redis.cache.RedisCacheManager;importorg.springframework.data.redis.connection.RedisConnectionFactory;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;importorg.springframework.data.redis.serializer.RedisSerializationContext;importorg.springframework.data.redis.serializer.StringRedisSerializer;importstaticorg.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig;/***@Author:JCccc*@CreateTime:2018-09-11*@Description:*/@Configuration@EnableCachingpublicclassRedisConfig{@BeanpublicCacheManagercacheManager(RedisConnectionFactoryconnectionFactory){RedisCacheConfigurationcacheConfiguration=defaultCacheConfig().disableCachingNullValues().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(newJackson2JsonRedisSerializer(Object.class)));returnRedisCacheManager.builder(connectionFactory).cacheDefaults(cacheConfiguration).build();}@BeanpublicRedisTemplate<String,Object>redisTemplate(RedisConnectionFactoryfactory){RedisTemplate<String,Object>redisTemplate=newRedisTemplate<>();redisTemplate.setConnectionFactory(factory);Jackson2JsonRedisSerializerjackson2JsonRedisSerializer=newJackson2JsonRedisSerializer(Object.class);ObjectMapperom=newObjectMapper();om.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);//序列化设置,这样为了存储操作对象时正常显示的数据,也能正常存储和获取redisTemplate.setKeySerializer(newStringRedisSerializer());redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.setHashKeySerializer(newStringRedisSerializer());redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);returnredisTemplate;}@BeanpublicStringRedisTemplatestringRedisTemplate(RedisConnectionFactoryfactory){StringRedisTemplatestringRedisTemplate=newStringRedisTemplate();stringRedisTemplate.setConnectionFactory(factory);returnstringRedisTemplate;}}

最后写测试接口,看看效果(一个POST,一个GET):

故意把时间放大,1000秒内重复调用,符合我们拦截规则的都会被拦截。

TestController.java

importcom.example.repeatdemo.dto.PayOrderApply;importcom.example.repeatdemo.annotation.RepeatDaMie;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.web.bind.annotation.*;/***@Author:JCccc*@Date:2022-6-059:44*@Description:*/@RestControllerpublicclassTestController{privatefinalLoggerlog=LoggerFactory.getLogger(this.getClass());@RepeatDaMie(second=1000,describe="尊敬的客户,您慢点")@PostMapping(value="/doPost")@ResponseBodypublicvoidtest(@RequestBodyPayOrderApplypayOrderApply){log.info("ControllerPOST请求:"+payOrderApply.toString());}@RepeatDaMie(second=1000,describe="大哥,你冷静点")@GetMapping(value="/doGet")@ResponseBodypublicvoiddoGet(PayOrderApplypayOrderApply){log.info("ControllerGET请求:"+payOrderApply.toString());}}

PayOrderApply.java

/***@Author:JCccc*@Date:2022-6-129:46*@Description:*/publicclassPayOrderApply{privateStringsn;privateLongamount;privateStringproCode;publicStringgetSn(){returnsn;}publicvoidsetSn(Stringsn){this.sn=sn;}publicLonggetAmount(){returnamount;}publicvoidsetAmount(Longamount){this.amount=amount;}publicStringgetProCode(){returnproCode;}publicvoidsetProCode(StringproCode){this.proCode=proCode;}@OverridepublicStringtoString(){return"PayOrderApply{"+"sn='"+sn+'\''+",amount="+amount+",proCode='"+proCode+'\''+'}';}}

Springboot怎么利用Redis实现接口幂等性拦截

redis生成了值:

Springboot怎么利用Redis实现接口幂等性拦截

Springboot怎么利用Redis实现接口幂等性拦截

 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:Springboot怎么利用Redis实现接口幂等性拦截的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:如何通过可写入的etcpasswd文件实现Linux提权下一篇:

27 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18