SpringBoot使用Session防止表单重复提交(提供Gitee源码)

news/2024/11/30 9:36:53/

前言:在日常开发中,客户可能会存在反复点击提交按钮导致表单的重复提交,这个问题也是非常需要重视的,在本篇博客中,采用的是session、自定义注解和拦截器的方式来防止重复表单的重复提交,提高整体代码的优雅和整洁度!

目录

一、核心实现思路

二、代码实现

1、编写自定义注解@RepeatSubmit

2、核心配置类

3、撰写拦截器核心代码

4、JSON工具类

5、编写请求层

6、运行结果

三、Gitee源码


一、核心实现思路

这边我用自己的理解方式进行的梳理,大概逻辑如下:

1、撰写一个自定义注解。

2、撰写自定义拦截器。

3、每次有请求之前都会进入此拦截器,判断当前的接口是否存在自定义的注解,不存在则直接放行请求。

4、比对当前传入的参数和之前存入session的参数以及时间差的别对是否成立,成立则为重复请求,拦截当前url请求,并返回错误信息。

二、代码实现

整体代码实现非常简洁以及通俗易懂,都是本人精心整理出来的。

1、编写自定义注解@RepeatSubmit

package com.example.repeat.annotation;import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
{/*** 间隔时间(ms),小于此时间视为重复提交*/public int interval() default 5000;/*** 提示消息*/public String message() default "不允许重复提交,请稍后再试";
}

2、核心配置类

package com.example.repeat.config;import com.example.repeat.interceptor.RepeatSubmitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class ResourcesConfig implements WebMvcConfigurer
{@Autowiredprivate RepeatSubmitInterceptor repeatSubmitInterceptor;/*** 自定义拦截规则*/@Overridepublic void addInterceptors(InterceptorRegistry registry){registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");}
}

3、撰写拦截器核心代码

package com.example.repeat.interceptor;import com.example.repeat.annotation.RepeatSubmit;
import com.example.repeat.utils.JSON;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {public final String REPEAT_PARAMS = "repeatParams";public final String REPEAT_TIME = "repeatTime";public final String SESSION_REPEAT_KEY = "repeatData";@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (handler instanceof HandlerMethod){HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);if (annotation != null){if (this.isRepeatSubmit(request, annotation)){System.out.println(annotation.message());return false;}}return true;}else{return true;}}public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) throws Exception{// 本次参数及系统时间String nowParams = JSON.marshal(request.getParameterMap());Map<String, Object> nowDataMap = new HashMap<String, Object>();nowDataMap.put(REPEAT_PARAMS, nowParams);nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());// 请求地址(作为存放session的key值)String url = request.getRequestURI();HttpSession session = request.getSession();Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY);if (sessionObj != null){Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;if (sessionMap.containsKey(url)){Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())){return true;}}}Map<String, Object> sessionMap = new HashMap<String, Object>();sessionMap.put(url, nowDataMap);session.setAttribute(SESSION_REPEAT_KEY, sessionMap);return false;}/*** 判断参数是否相同*/private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap){String nowParams = (String) nowMap.get(REPEAT_PARAMS);String preParams = (String) preMap.get(REPEAT_PARAMS);return nowParams.equals(preParams);}/*** 判断两次间隔时间*/private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval){long time1 = (Long) nowMap.get(REPEAT_TIME);long time2 = (Long) preMap.get(REPEAT_TIME);if ((time1 - time2) < interval){return true;}return false;}
}

4、JSON工具类

package com.example.repeat.utils;import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;public class JSON
{public static final String DEFAULT_FAIL = "\"Parse failed\"";private static final ObjectMapper objectMapper = new ObjectMapper();private static final ObjectWriter objectWriter = objectMapper.writerWithDefaultPrettyPrinter();public static void marshal(File file, Object value) throws Exception{try{objectWriter.writeValue(file, value);}catch (JsonGenerationException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static void marshal(OutputStream os, Object value) throws Exception{try{objectWriter.writeValue(os, value);}catch (JsonGenerationException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static String marshal(Object value) throws Exception{try{return objectWriter.writeValueAsString(value);}catch (JsonGenerationException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static byte[] marshalBytes(Object value) throws Exception{try{return objectWriter.writeValueAsBytes(value);}catch (JsonGenerationException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static <T> T unmarshal(File file, Class<T> valueType) throws Exception{try{return objectMapper.readValue(file, valueType);}catch (JsonParseException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static <T> T unmarshal(InputStream is, Class<T> valueType) throws Exception{try{return objectMapper.readValue(is, valueType);}catch (JsonParseException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static <T> T unmarshal(String str, Class<T> valueType) throws Exception{try{return objectMapper.readValue(str, valueType);}catch (JsonParseException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static <T> T unmarshal(byte[] bytes, Class<T> valueType) throws Exception{try{if (bytes == null){bytes = new byte[0];}return objectMapper.readValue(bytes, 0, bytes.length, valueType);}catch (JsonParseException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}
}

5、编写请求层

package com.example.repeat.controller;import com.example.repeat.annotation.RepeatSubmit;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@RepeatSubmit@RequestMapping("/hello")public String hello(){return "hello";}
}

6、运行结果

这边使用了get请求,为了图方便,在浏览器不断请求,后台输出如下! 

 

三、Gitee源码

SpringBoot使用Session防止表单重复提交: 在日常开发中,客户可能会存在反复点击提交按钮导致表单的重复提交,这个问题也是非常需要重视的,在本篇博客中,采用的是session、自定义注解和拦截器的方式来防止重复表单的重复提交,提高整体代码的优雅和整洁度!


http://www.ppmy.cn/news/494541.html

相关文章

笔记本连接蓝牙音箱声音异常

问题&#xff1a; 笔记本连接蓝牙音箱声音异常。蓝牙音箱不播放或者声音很小。 蓝牙音箱和电脑连接过程&#xff1a; 如下图&#xff1a; 如果电脑播放歌曲&#xff0c;蓝牙音箱没有声音。 解决方法有如下&#xff1a; &#xff08;1&#xff09;声音按钮右键》声音》播放》h8 …

记一次蓝牙音箱无声音的原因

现象 音箱已连接&#xff0c;并且音量处有音量的波动。但是实际上并没有发出声音。 系统 PC win11 排查 1. 在声音设备中发现该音箱同时作为了输入设备&#xff0c;但其实该音箱是没有输入的功能的 2. 点击进去选择不允许&#xff0c;问题得到解决。

linux命令行连接蓝牙音箱,有些Linux发行版用蓝牙连接天猫精灵和小爱音箱没声音...

在Linux下用蓝牙连接天猫精灵和小爱音箱有些Linux发行版有声音&#xff0c;有些则没有声音发出。有些跟声音设置有关&#xff0c;有些则是跟Linux发行版有关&#xff0c;有些跟音箱的型号有关。比如使用的是联想小新Air12笔记本&#xff0c;已经连接上了小爱音箱&#xff0c;在…

Ubuntu20.04 外接音响播放音频时出现杂音或滋滋滋电流声解决方法

Ubuntu20.04 外接音响播放音乐时出现杂音解决方法 引言在Ubuntu 20.04及以上版本中使用Pipewire。更新系统内核等安装最新的Pipewire库用Pipewire替换PulseAudio验证 Pipewire 安装 引言 当更新系统版本至20.04或更高版本后&#xff0c;有可能会出现声卡驱动版本不兼容的情形&…

笔记本连接显示器后没有声音_外接显示器后没声音怎么回事

外接显示器后没声音怎么回事 外接显示器后没声音的解决方法&#xff1a; 1. 调整显示器的输入模式和笔记本的输出模式。 用 hdmi 线连上显示器以后&#xff0c;需要吧显示器调成 hdmi 输入模 式 (hdmi input mode) &#xff0c;通常显示器都会有快捷的按钮切换输入模 式的。 然…

5.1.2全景声音箱摆位_杜比全景声7.1.2和7.1.4音箱摆位有什么区别

马上注册,结交更多烧友,大家一起分享交流,快心快哉。 您需要 登录 才可以下载或查看,没有帐号?快速注册 x 最近,不管是QQ、微信、论坛都有人问我,7.1.4和7.1.2到底有什么区别。我本来准备开新贴讲,算了,就在这个贴子里面,解决。我可以保证这能够百分之百提起你对7.1.…

Cortext-M3系列:M3的底层编程(7)

1、概览 在CM3上编程&#xff0c;既可以使用C也可以使用汇编&#xff0c;keil也支持C&#xff0c;但是大多数人还是会在C与汇编。C与汇编都“尺有所短&#xff0c;寸有所长”&#xff0c;不能互相取代。使用C能开发大型程序&#xff0c;而汇编则用于执行特种任务。 1.1 使用汇…

php用什么电脑,我要学php了买一台什么配置的电脑最好?

回答&#xff1a; 其实土木本科生并没有特别的电脑需求。也就是普通主流配置电脑就行了。 诚然有那些很高大上的软件&#xff0c;如SAP2000、ETABS&#xff0c;还有一些列通用有限元分析软件&#xff0c;以及非常吃硬件的BIM软件&#xff0c;但本科生一来不会学、二来学校也不会…