防止表单重复提交的问题

2024-09-01 12:38

本文主要是介绍防止表单重复提交的问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近老大让我处理一下订单重复提交的问题,不会做,自己网上默默的查资料,发现各式各样的,然后自己整理成一下,方便以后用。

 

首先我们分析下原因:

1、在网络延迟的情况下让用户有时间点击多次提交按钮导致表单重复提交。

2、表单提交后用户点击浏览器的刷新导致表单重复提交

3、用户提交表单后,点击浏览器的【后退】按钮回退到表单页面后进行再次提交

 

总得来说都是,服务器在来不及处理的情况下进行了多次操作。那要这么解决呢?

 

1、用JavaScript的方式在客户端处理。

a、设置一个标识,让他只能提交一次

 

var isCommitted = false;//表单是否已经提交标识,默认为false
function dosubmit(){if(isCommitted==false){isCommitted = true;//提交表单后,将表单是否已经提交标识设置为truereturn true;//返回true让表单正常提交}else{return false;//返回false那么表单将不提交}
}

b、表单提交之后,将提交按钮设置为不可用,让用户没有机会点击第二次提交按钮
 

 

注:但是使用JavaScript防止表单重复提交的做法只对上述提交到导致表单重复提交第一中原因有效,对后两种,依然无法解决表单重复提交问题。

 

2、利用Session防止表单重复提交

 

做法:在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
  在下列情况下,服务器程序将拒绝处理用户提交的表单请求:

  1. 存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
  2. 当前用户的Session中不存在Token(令牌)
  3. 用户提交的表单数据中没有Token(令牌)

看具体的范例:

  1.创建FormServlet,用于生成Token(令牌)和跳转到form.jsp页面

 

package xdp.gacl.session;import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class FormServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {request.getSession().setAttribute("token", <span class="crayon-h"></span><span class="crayon-v">UUID</span><span class="crayon-sy">.</span><span class="crayon-e">randomUUID</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span><span class="crayon-sy">.</span><span class="crayon-e">toString</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span>);  //在服务器使用session保存token(令牌)request.getRequestDispatcher("/form.jsp").forward(request, response);//跳转到form.jsp页面}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

 

 

2、在jsp页面中的form表单中隐藏域来存储Token(令牌)

 

<input type="hidden" name="token" value="${token}"/>
 

 

DoFormServlet处理表单提交

 

public class DoFormServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {boolean temp = isRepeatSubmit(request);//判断用户是否是重复提交if(temp){System.out.println("请不要重复提交");return;}request.getSession().removeAttribute("token");//移除session中的tokenSystem.out.println("处理用户提交请求!!");}/*** 判断客户端提交上来的令牌和服务器端生成的令牌是否一致* @param request* @return *         true 用户重复提交了表单 *         false 用户没有重复提交表单*/private boolean isRepeatSubmit(HttpServletRequest request) {String client_token = request.getParameter("token");//1、如果用户提交的表单数据中没有token,则用户是重复提交了表单if(client_token==null){return true;}//取出存储在Session中的tokenString server_token = (String) request.getSession().getAttribute("token");//2、如果当前用户的Session中不存在Token(令牌),则用户是重复提交了表单if(server_token==null){return true;}//3、存储在Session中的Token(令牌)与表单提交的Token(令牌)不同,则用户是重复提交了表单if(!client_token.equals(server_token)){return true;}return false;}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}
 

 

从运行效果中可以看到,通过这种方式处理表单重复提交,可以解决第2种和第3中原因的表单重复提交问题。

 

3、用springMVC的拦截器+注解的方式

a、自定义注解Token代码

 

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Token {boolean save() default false;boolean remove() default false;
}

b、拦截器TokenInterceptor代码:

 

 

public class TokenInterceptor extends HandlerInterceptorAdapter {/** 生成一个唯一值的token*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();Token annotation = method.getAnnotation(Token.class);if (annotation != null) {boolean needSaveSession = annotation.needSaveToken();// 在会话中存放一个key=token的令牌if (needSaveSession) {request.getSession(false).setAttribute("token", UUID.randomUUID().toString());}boolean needRemoveSession = annotation.needRemoveToken();// 移除令牌,是令牌失效if (needRemoveSession) {// 验证是否重复提交if (isRepeatSubmit(request)) {return false;}request.getSession(false).removeAttribute("token");}}return true;} else {return super.preHandle(request, response, handler);}}// 验证表单token值和session中的token值是否一致private boolean isRepeatSubmit(HttpServletRequest request) {String serverToken = (String) request.getSession(false).getAttribute("token");if (serverToken == null) {return true;}String clinetToken = request.getParameter("token");if (clinetToken == null) {return true;}if (!serverToken.equals(clinetToken)) {return true;}return false;}
}

c、spring 的配置文件

 

 

<!--配置springmvc拦截器,用于负责拦截订单提交  --><bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"><property name="interceptors"><list><bean class="com.jzt.common.dao.mybatis.interceptor.TokenInterceptor"/></list></property></bean>

d、在相关方法中加入注解:

 

 

@Token(needSaveToken = true)
public ModelAndView nextCart(@ObjectConvertAnno HttpParameterParser httpParser) {
}@Token(needSaveToken = true)public ModelAndView nextCart(@ObjectConvertAnno HttpParameterParser httpParser) {
}
注意
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
这个里面的handler是目标controler,而不是方法。
执行到
HandlerMethod handlerMethod = (HandlerMethod) handler;
的时候就挂了,类型转换错误。
就将
spring 配置里面应该有这个handler :
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
换成:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
对应的应该也有一个handlerAdapter:
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
换成:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
还有就是spring的版本如果不支持的话,就提高一下。
4、对上种方式进行修改,不再将令牌存入session中,而是放入第三方缓存中。

因为在大型公司的网站,一般不只是放在同一台服务器,那就可能还会出现重复提交的问题,因为如果他访问的不是我们存有令牌的服务器,

它就会绕过我们的令牌,相当于令牌失效。所以我们存入第三方缓存中。

 

备注:session是由一个对应的id存在服务器中,所以session的存放是在服务器上,

 


 

 

 

 

 

 

 

 

 

 

这篇关于防止表单重复提交的问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1126961

相关文章

MySQL中查找重复值的实现

《MySQL中查找重复值的实现》查找重复值是一项常见需求,比如在数据清理、数据分析、数据质量检查等场景下,我们常常需要找出表中某列或多列的重复值,具有一定的参考价值,感兴趣的可以了解一下... 目录技术背景实现步骤方法一:使用GROUP BY和HAVING子句方法二:仅返回重复值方法三:返回完整记录方法四:

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

Springboot如何正确使用AOP问题

《Springboot如何正确使用AOP问题》:本文主要介绍Springboot如何正确使用AOP问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录​一、AOP概念二、切点表达式​execution表达式案例三、AOP通知四、springboot中使用AOP导出

Python中Tensorflow无法调用GPU问题的解决方法

《Python中Tensorflow无法调用GPU问题的解决方法》文章详解如何解决TensorFlow在Windows无法识别GPU的问题,需降级至2.10版本,安装匹配CUDA11.2和cuDNN... 当用以下代码查看GPU数量时,gpuspython返回的是一个空列表,说明tensorflow没有找到

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

XML重复查询一条Sql语句的解决方法

《XML重复查询一条Sql语句的解决方法》文章分析了XML重复查询与日志失效问题,指出因DTO缺少@Data注解导致日志无法格式化、空指针风险及参数穿透,进而引发性能灾难,解决方案为在Controll... 目录一、核心问题:从SQL重复执行到日志失效二、根因剖析:DTO断裂引发的级联故障三、解决方案:修复

IDEA Maven提示:未解析的依赖项的问题及解决

《IDEAMaven提示:未解析的依赖项的问题及解决》:本文主要介绍IDEAMaven提示:未解析的依赖项的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录IDEA Maven提示:未解析的依编程赖项例如总结IDEA Maven提示:未解析的依赖项例如