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

防止请求重复提交:注解+拦截器的实现方案

文章目录

    • 了解请求重复提交
    • 解决思路
    • 具体实现

了解请求重复提交

请求重复提交是指用户在一次请求还未处理完成时,重复提交了相同的请求。这种情况通常发生在网络延迟、用户误操作或系统性能不佳等情况下。

请求重复提交可能会导致以下问题和影响:

  1. 数据不一致:如果重复提交的请求包含了对数据的修改操作,那么可能会导致数据不一致的问题,例如重复购买商品、重复支付订单等。

  2. 系统资源浪费:重复提交的请求会占用系统资源,导致服务器负载过高、响应时间变慢等问题,影响系统性能和用户体验。

  3. 安全问题:某些敏感操作(如支付、修改密码等)如果被重复提交,可能会导致安全问题,例如重复支付导致资金损失、重复修改密码导致账号被盗等。

因此,防止请求重复提交是保证系统数据一致性、提高系统性能和保障系统安全的重要措施。

解决思路

在 Spring Boot 中有效地防止用户在短时间内重复提交表单或请求,我们可以结合 Redis 来实现这个功能,我们可以将用户的请求信息存储在缓存中,这样我们就可以实时跟踪用户请求的状态,同时也可以提高系统的性能。为了限制用户在短时间内重复提交相同的请求,我们可以设置一个时间间隔来限制重复提交。用户在指定时间间隔内提交相同的请求时,将返回一个错误信息,否则请求将被正常处理。因此,通过这个方法,我们可以有效地防止用户在短时间内提交重复的请求,避免系统资源的浪费。通常情况下,我们可以使用注解+拦截器的方式来实现这个功能。

注解是一种在代码中添加元数据的方式,它可以为方法、类、字段等添加额外的信息。在请求重复提交的场景中,我们可以通过在方法或接口上添加自定义注解,在运行时通过反射机制来获取并解析注解信息,从而执行相应的逻辑。

而拦截器则是一种在请求到达控制器之前对请求进行处理的组件。它可以拦截请求,并在请求到达目标处理程序之前或之后执行一些预处理或后处理操作。

具体工作流程图如下:

image-20231028152416711

具体实现

  1. 首先我们需要创建一个自定义注解,用于标记需要进行重复请求拦截的方法。例如,我们在项目中创建一个名为@RepeatSubmit的注解:

    @Inherited
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RepeatSubmit {/*** 间隔时间(ms),小于此时间视为重复提交*/public int interval() default 60000;/*** 提示消息*/public String message() default "不允许重复提交,请稍候再试";}
    
  2. 创建一个拦截器类,用于拦截带有@RepeatSubmit注解的请求。在拦截器中,可以通过保存请求的唯一标识(如token)来判断请求是否已经被处理过。

    @Component
    public class RepeatSubmitInterceptor implements HandlerInterceptor {@Autowiredprivate RedisCache redisCache;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 判断处理器对象是否为HandlerMethod类型if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();// 获取方法上的RepeatSubmit注解RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);if (annotation != null) {// 获取请求的IP地址、URI、请求方法、请求参数和请求体String ipAddr = IpUtils.getIpAddr(request);String requestURI = request.getRequestURI();String requestMethod = request.getMethod();Map<String, String> requestParams = ServletUtils.getRequestParams(request);Map<String, String> requestBody = ServletUtils.getRequestBody(request);// 创建缓存数据对象,并将请求信息存入其中Map<String,Object> cacheData = new HashMap<>();cacheData.put("ip",ipAddr);cacheData.put("uri",requestURI);cacheData.put("method",requestMethod);cacheData.put("params",requestParams);cacheData.put("body",requestBody);// 将缓存数据进行MD5加密String md5V = MD5Utils.encrypt(cacheData);// 在Redis缓存中设置缓存对象,如果已存在则返回falseBoolean b = redisCache.setCacheObjectIfAbsent("repeat_submit:"+ipAddr+":"+md5V, cacheData, annotation.interval(), TimeUnit.MILLISECONDS);if(b){// 如果缓存设置成功,则放行请求return true;}else {// 如果缓存已存在,则返回重复提交错误信息AjaxResult ajaxResult = AjaxResult.error(annotation.message());ServletUtils.renderString(response, JSON.toJSONString(ajaxResult));return false;}}// 如果方法没有RepeatSubmit注解,则放行请求return true;}else {// 如果处理器对象不是HandlerMethod类型,则放行请求return true;}}}
    
  3. 在Spring Boot的配置类中注册拦截器:

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {@Autowiredprivate RepeatSubmitInterceptor repeatSubmitInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");}}
    
  4. 在需要进行重复请求拦截的方法上添加@DuplicateSubmitToken注解:

    @Controller
    public class MyController {@RequestMapping("/submit")@RepeatSubmitpublic String submit() {// 处理请求逻辑return "success";}
    }
    
  5. 启动项目,使用 ApiFox 发起请求访问/submit接口,发送成功:

    image-20231028144719930

  6. 请求发送成功后,查看Redis中的缓存数据,包含我们请求时接口数据:

    image-20231028144758402

  7. 再次发起同样的请求,直接返回提示信息“不允许重复提交,请稍候再试”:

    image-20231028144902473

相关文章:

防止请求重复提交:注解+拦截器的实现方案

文章目录 了解请求重复提交解决思路具体实现 了解请求重复提交 请求重复提交是指用户在一次请求还未处理完成时&#xff0c;重复提交了相同的请求。这种情况通常发生在网络延迟、用户误操作或系统性能不佳等情况下。 请求重复提交可能会导致以下问题和影响&#xff1a; 数据不…...

C#使用mysql-connector-net驱动连接mariadb报错

给树莓派用最新的官方OS重刷了一下&#xff0c;并且用apt install mariadb-server装上“mysql”作为我的测试服务器。然后神奇的事情发生了&#xff0c;之前用得好好的程序突然就报错了&#xff0c;经过排查&#xff0c;发现在连接数据库的Open阶段就报错了。写了个最单纯的Con…...

SpringBoot 定时任务:@EnableScheduling @Scheduled

Scheduled注解参数 cron参数 这个参数是最经常使用的参数&#xff0c;表示接收一个cron参数&#xff0c;cron它是一个表达式&#xff0c;最多接收7个参数&#xff0c;从左到右分别表示&#xff1a;秒 分 时 天 月 周 年&#xff1b;参数以空格隔开&#xff0c;其中年不是必须参…...

Jquery 如何获取子元素。如何找到所有 HTML select 标签的选中项。jQuery 里的 ID 选择器和 class 选择器有何不同

可以使用 jQuery 的子选择器&#xff08;Child Selector&#xff09;或 find() 方法来获取子元素。 子选择器&#xff08;Child Selector&#xff09;&#xff1a; 使用父元素的选择器和 > 符号来选取该父元素的子元素。 例如&#xff1a;选取 id 为 parent 的元素内所有 cl…...

Python Selenium 之数据驱动测试的实现!

数据驱动模式的测试好处相比普通模式的测试就显而易见了吧&#xff01;使用数据驱动的模式&#xff0c;可以根据业务分解测试数据&#xff0c;只需定义变量&#xff0c;使用外部或者自定义的数据使其参数化&#xff0c;从而避免了使用之前测试脚本中固定的数据。可以将测试脚本…...

【Proteus仿真】【STM32单片机】智能语音家居陪护机器人

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用OLED显示模块、红外传感器、蜂鸣器、DS18B20温度传感器&#xff0c;风扇LED、语音识别模块等。 主要功能&#xff1a; 系统运行后&#xff0c;…...

C#上位机序列10: 批量读写+点对点更新+数据类型处理

一、源码结构 二、运行效果 三、源码解析 PLC批量读写点对点更新数据类型处理 优点&#xff1a;根据数据类型&#xff0c;判定监听的地址范围&#xff08;40120_int 监听两个word&#xff1a;40120 40121&#xff1b;40130_long 监听四个word&#xff1a;40130 40131 40132 4…...

MySQL 概述 数据库表操作 数据增删改

目录 MySQL概述前言安装与配置MySQL登录与卸载 数据模型概述SQL简介SQL通用语法简介SQL分类 数据库设计(数据库操作)-DDL数据库操作查询数据库 show databases、select database()创建数据库 create database使用数据库 use删除数据库 drop database 图形化工具连接数据库操作数…...

存储器概述

一、存储系统基本概念...

Fabric.js 使用自定义字体

本文简介 点赞 关注 收藏 学会了 如果你使用 Fabric.js 做编辑类的产品&#xff0c;有可能需要给用户配置字体。 这次就讲讲在 Fabric.js 中创建文本时怎么使用自定义字体、在项目运行时怎么修改字体、以及推荐一个精简字体库的工具。 学习本文前&#xff0c;你必须有一点…...

【C++项目】高并发内存池第七讲性能分析

目录 1.测试代码2.代码介绍3.运行结结果 1.测试代码 #include"ConcurrentAlloc.h" #include"ObjectPool.h" #include"Common.h" void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds) {std::vector<std::thread> vthread(…...

【JavaScript】快速学习JS

JS区分大小写&#xff0c;后面的分号可有可无&#xff1b; 输出语句 window.alter() // 写入警告框&#xff1b;在浏览器中的警告弹窗输出 document.write() // 写入html输出&#xff1b;在html页面中输出 console.log() // 写入浏览器控制台&#xff1b;在控制台输出 变量…...

控制输入流,从控制台打印到文件中,更改输出的位置

public static void main(String[] args) throws IOException {PrintStream printStream System.out;//在默认情况下&#xff0c;PrintStream 输出数据的位置 标准输出&#xff0c;即显示器printStream.print("Tom,hello");/*public void print(String s) {if (s n…...

计算线阵相机 到 拍摄产品之间 摆放距离?(隐含条件:保证图像不变形)

一物体被放置在传送带上&#xff0c;转轴的直径为100mm。已知线阵相机4K7u&#xff08;一行共4096个像素单元&#xff0c;像素单元大小7um&#xff09;&#xff0c;镜头35mm&#xff0c;编码器2000脉冲/圈。保证图像不变形的条件下&#xff0c;计算相机到产品之间 摆放距离&…...

【网络】详解http协议

目录 一、认识URLurlencode和urldecode 二、HTTP协议HTTP协议格式HTTP的方法HTTP的状态码HTTP常见Header 一、认识URL URL叫做统一资源定位符&#xff0c;也就是我们平时俗称的网址&#xff0c;是因特网的万维网服务程序上用于指定信息位置的表示方法。 urlencode和urldecode …...

1819_ChibiOS的互斥信号与条件变量

全部学习汇总&#xff1a; GreyZhang/g_ChibiOS: I found a new RTOS called ChibiOS and it seems interesting! (github.com) 1. 关于会吃信号与条件变量的全局配置提供了4个配置信息&#xff0c;分别是互斥信号的使能、互斥信号的递归支持、条件变量的使能、条件变量的超时使…...

CSDN学院 < 华为战略方法论进阶课 > 正式上线!

目录 你将收获 适用人群 课程内容 内容目录 CSDN学院 作者简介 你将收获 提升职场技能提升战略规划的能力实现多元化发展综合能力进阶 适用人群 主要适合公司中高层、创业者、产品经理、咨询顾问&#xff0c;以及致力于改变现状的学员。 课程内容 本期课程主要介绍华为…...

电商接口api数据比价接口推荐

当前&#xff0c;受诸多因素的影响&#xff0c;经济下行&#xff0c;在日趋激烈的市场竞争中&#xff0c;很多企业也都面临着越来越大的生存压力&#xff0c;企业的盈利空间也逐渐被压缩。因此&#xff0c;越来越多的企业在控制成本方面更下功夫&#xff0c;这也就对企业采购提…...

读取Excel的工具类——ExcelKit

文章目录 ExcelKit工具类1、准备工作1.1、SheetInfoVo1.2、BizException 2、读取xlsx3、读取xls4、完整的ExcelKit.java源码 ExcelKit工具类 1、准备工作 1.1、SheetInfoVo import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import …...

vscode连接服务器一直retry

解决方法 打开vscode控制面板&#xff0c;输入命令remote-ssh: kill vs code server on host 选择一直连接不上的服务器端口 重新连接...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关

在水泥厂的生产流程中&#xff0c;工业自动化网关起着至关重要的作用&#xff0c;尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关&#xff0c;为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多&#xff0c;其中不少设备采用Devicenet协议。Devicen…...