当前位置: 首页 > news >正文

SpringBoot拦截器防重复提交实战

springboot防重复提交实现

    • 自定义注解
    • 拦截器逻辑
    • 配置拦截器
    • 抛异常代码
  • 开发中可能会经常遇到短时间内由于用户的重复点击导致几秒之内重复的请求,可能就是在这几秒之内由于各种问题,比如 网络 ,事务的隔离性等等问题导致了数据的重复等问题,因此在日常开发中必须规
    避这类的重复请求操作,今天就用拦截器处理一下这个问题

自定义注解

packagetest;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public@interfaceRepeatSubmit{/** * 默认失效时间时间(秒),小于或等于0表示不启用 */longseconds()default5;}

拦截器逻辑

packagetest;importcom.ruoyi.common.exception.RepeatSubmitException;importcom.sun.org.apache.xpath.internal.operations.Bool;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.core.annotation.AnnotationUtils;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.stereotype.Component;importorg.springframework.util.StringUtils;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.HandlerInterceptor;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.util.Objects;importjava.util.concurrent.TimeUnit;/** * 重复请求的拦截器 * * @Component:注解将当前类注入到IOC容器中 */@ComponentpublicclassRepeatSubmitInterceptorimplementsHandlerInterceptor{@AutowiredprivateStringRedisTemplatestringRedisTemplate;@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{if(handlerinstanceofHandlerMethod){//只拦截@RepeatSubmit注解HandlerMethodmethod=(HandlerMethod)handler;//标注在方法上的注解RepeatSubmitrepeatSubmitByMethod=AnnotationUtils.findAnnotation(method.getMethod(),RepeatSubmit.class);//标注在类上的注解RepeatSubmitrepeatSubmitByCls=AnnotationUtils.findAnnotation(method.getMethod().getDeclaringClass(),RepeatSubmit.class);//组合判断条件,根据自己项目实际需求来,这里只用简单的身份标识做拦截示例//没有限制重复提交,直接跳过if(Objects.isNull(repeatSubmitByMethod)&&Objects.isNull(repeatSubmitByCls))returntrue;//优先使用方法级注解,其次使用类级注解RepeatSubmitrepeatSubmit=Objects.nonNull(repeatSubmitByMethod)?repeatSubmitByMethod:repeatSubmitByCls;//验证注解配置的有效性:失效时间必须大于0if(repeatSubmit.seconds()<=0){returntrue;}//请求urlStringuri=request.getRequestURI();//构建更精确的Redis Key:结合用户标识防止不同用户间误判StringuserKey=getUserIdentifier(request);StringredisKey=userKey+":"+uri;//redis中存在返回false,不存在返回trueBooleanifAbsent=stringRedisTemplate.opsForValue().setIfAbsent(redisKey,"1",Objects.nonNull(repeatSubmitByMethod)?repeatSubmitByMethod.seconds():repeatSubmitByCls.seconds(),TimeUnit.SECONDS);//如果存在,表示已经请求过了,直接抛出异常,由全局异常进行处理返回指定信息if(ifAbsent!=null&&!ifAbsent){thrownewRepeatSubmitException();}returntrue;}returnHandlerInterceptor.super.preHandle(request,response,handler);}/** * 获取用户唯一标识 * 优先级:Token > SessionId > IP地址 */privateStringgetUserIdentifier(HttpServletRequestrequest){//尝试从请求头获取TokenStringtoken=request.getHeader("Authorization");if(StringUtils.hasText(token)){returntoken;}//尝试获取SessionIdStringsessionId=request.getRequestedSessionId();if(StringUtils.hasText(sessionId)){returnsessionId;}//使用IP地址作为兜底方案Stringip=getClientIp(request);returnStringUtils.hasText(ip)?ip:"unknown";}/** * 获取客户端真实IP */privateStringgetClientIp(HttpServletRequestrequest){Stringip=request.getHeader("X-Forwarded-For");if(StringUtils.hasText(ip)&&!"unknown".equalsIgnoreCase(ip)){//多次反向代理后会有多个IP值,第一个为真实IPintindex=ip.indexOf(',');if(index!=-1){returnip.substring(0,index);}returnip;}ip=request.getHeader("X-Real-IP");if(StringUtils.hasText(ip)&&!"unknown".equalsIgnoreCase(ip)){returnip;}ip=request.getHeader("Proxy-Client-IP");if(StringUtils.hasText(ip)&&!"unknown".equalsIgnoreCase(ip)){returnip;}ip=request.getHeader("WL-Proxy-Client-IP");if(StringUtils.hasText(ip)&&!"unknown".equalsIgnoreCase(ip)){returnip;}returnrequest.getRemoteAddr();}}

配置拦截器

packagetest;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;publicclassWebConfigimplementsWebMvcConfigurer{@AutowiredprivateRepeatSubmitInterceptorrepeatSubmitInterceptor;@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){finalString[]commonExclude={"/error","/files/**"};registry.addInterceptor(repeatSubmitInterceptor).excludePathPatterns(commonExclude);}}

抛异常代码

packagecom.ruoyi.common.exception;/** * 重复提交异常 * * @author ruoyi */publicclassRepeatSubmitExceptionextendsRuntimeException{privatestaticfinallongserialVersionUID=1L;/** * 错误提示 */privateStringmessage;/** * 空构造方法 */publicRepeatSubmitException(){this("不允许重复提交,请稍后再试");}publicRepeatSubmitException(Stringmessage){super(message);this.message=message;}@OverridepublicStringgetMessage(){returnmessage;}publicRepeatSubmitExceptionsetMessage(Stringmessage){this.message=message;returnthis;}}
http://www.jsqmd.com/news/954199/

相关文章:

  • 智慧树刷课插件:3分钟配置实现视频自动连播的终极解决方案
  • 别再让LabVIEW程序乱跑了!用顺序结构给你的数据流编程上把‘锁’
  • 2026培育钻婚戒怎么选?6大品牌横评,附避坑指南 - GrowthUME
  • 企业选购沈阳小程序开发厂家时关注的5个关键评估点
  • 3秒获取百度网盘提取码:告别繁琐搜索,拥抱高效资源获取新时代
  • NVIDIA Profile Inspector终极指南:解锁隐藏显卡设置的完整教程
  • 北京石景山区黄金回收简报 本地市场趋势与机构选择 - 上门黄金回收
  • 智慧树自动化学习助手:3步配置实现视频自动连播终极方案
  • 告别手动配置:用Netplan和systemd-resolved在Debian10上优雅管理网络与DNS
  • 2026舟山市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐
  • 广东广西工业气体供应商有哪些代表公司?2026年采购核验与对比指南 - 广州矩阵架构科技公司
  • 2026杭州室内游玩乐园畅玩攻略|告别酷暑阴雨一站式解锁未来城市乐园 - 资讯速览
  • 2026 赣州防水补漏三家品牌横向测评:厨卫屋面地下室修缮哪家靠谱?吉修匠 99.8 分五星稳居榜首 - 吉修匠
  • 无锡北塘区暗管漏水检测上门服务,精准定位漏点,微创维修不破坏地面 - 同城资讯
  • 二〇二六年石家庄六家正规黄金回收机构实测:权威背书下的口碑门店深度测评 - 薛定谔的梨花猫
  • 手机相机模组出厂前必做的‘体检’:深入拆解OTP与LSC校准流程
  • MATLAB写的五子棋单机版,带鼠标下棋、悔棋和自动胜负判断
  • 保姆级教程:用QGIS 3.28把Excel气象数据变成专业色斑图(附数据下载)
  • 运放选型避坑指南:除了GBW和压摆率,稳定性参数(相位裕度)你关注了吗?
  • 西安市黄金回收白银回收铂金回收彩金回收门店优选+2026年最新黄金回收TOP5排行榜靠谱商家推荐及联系方式 - 亦辰小黄鸭
  • Zynq PL-PS交互实战:用AXI GPIO中断实现按键控制LED(附完整SDK代码与调试技巧)
  • 营口市外网管道漏水检测、消防、供暖、自来水、埋地管道测漏、精准定位 抢修 - 天堂海洋
  • 微信怎么发起活动报名?云众评选全流程教程 - 微信投票小程序
  • 从‘分不清’到‘分得清’:用粗糙集思想帮你搞定混乱的业务数据分类难题
  • 有哪些真正好用的降AI率网站?能同时压低重复率和减少机器写作感的那种 - 降AI小能手
  • 从Simulink仿真到实战:手把手教你调一个无静差的直流电机PI调速闭环
  • 深入Zynq PS-PL交互:拆解AXI GPIO软核,对比硬核GPIO与AXI_HP/GP接口选型
  • 破解山洪防御难题,从GIS水文分析到HEC-RAS建模:山洪径流模拟与危险性评价全流程实战指南
  • 培育钻石婚戒市场深度洞察:6大品牌对比评测,解锁你的理想之选 - GrowthUME
  • 项目不同阶段怎么用Claude Code?一份来自真实项目的使用手册