- 浏览: 216069 次
最新评论
-
jin8000608172:
Profiler类是自己写的还是开源jar里面的,如果是开源j ...
isInfoEnabled究竟多有用? -
u011358822:
www.baidu.com
实战Concurrent -
u011358822:
[url][img][list][*]引用[/list][/i ...
实战Concurrent -
water_lang:
这本书我买了一本,但是我还是没找到该怎么解决事务这块,mong ...
《MongoDB实战》译者序 -
DREAM_UTOPIA:
每周推荐非常好,观点独到,涉及面广,谢谢
每周推荐阅读2013Q2汇总
前阵子弄了些表单防重复提交的东西,想整理整理,免得下次要用时再四处去找,其实这里的东西还是挺简单的。
原理:
在Session中保存一个表单的唯一编号,将该编号放在一个隐藏域中,同其他数据一同提交。在提交表单后,通过拦截器或其他机制检查唯一编号,如果存在则说明表单是第一次提交,如果不存在则被重复提交(理由很简单,在第一次提交检查后就会从Session中移除该编号)。保存编号可以用一个HashMap。
上代码:
表单类,用于保存表单创建时间和表单的标示
package form; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.builder.ToStringBuilder; /** * 表单类 * * @author DigitalSonic */ public class Form implements Serializable { /** * serialVersionUID */ private static final long serialVersionUID = 8796758608626021150L; public static final String FORM_UNIQ_ID_FIELD_NAME = "_form_uniq_id"; /** 表单标识*/ private String token; /** 表单创建时间*/ private Date createTime; /** * 构造函数 */ public Form(String token) { this.token = token; this.createTime = new Date(); } public String toString() { return ToStringBuilder.reflectionToString(this); } public String getToken() { return token; } public Date getCreateTime() { return createTime; } }
表单管理器接口
package form; import javax.servlet.http.HttpServletRequest; /** * 表单管理器,负责管理Session中的表单。 * * @author DigitalSonic */ public interface FormManager { /** * 生成一个新的表单 */ public Form newForm(HttpServletRequest request); /** * 判断表单是否存在。 */ public boolean hasForm(HttpServletRequest request, String token); /** * 访问参数中是否存在表单Token。 */ public boolean hasFormToken(HttpServletRequest request); /** * 销毁一个表单 */ public void destroyToken(HttpServletRequest request, String token); /** * 打印表单信息。 */ public String dumpForm(HttpServletRequest request, String token); }
表单管理器接口实现
package form; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.StringUtils /** * 表单管理器实现类 * * @author DigitalSonic */ public class FormManagerImpl implements FormManager { private static final String SESSION_KEY_OF_FROMS = "_forms_in_session"; /** 表单最大个数 */ private int maxFormNum = 7; /** * 销毁一个表单 */ public void destroyToken(HttpServletRequest request, String token) { getForms(request).remove(token); } /** * 打印表单信息。 */ public String dumpForm(HttpServletRequest request, String token) { Form form = getForms(request).get(token); if (form == null) { return "null"; } return form.toString(); } /** * 判断表单是否存在。如果token为null,直接返回false。 * * @see #getForms(HttpServletRequest) */ public boolean hasForm(HttpServletRequest request, String token) { if (token == null) { return false; } return getForms(request).containsKey(token); } /** * 访问参数中是否存在表单Token。 */ public boolean hasFormToken(HttpServletRequest request) { String formToken = request.getParameter(Form.FORM_TOKEN_FIELD_NAME); return StringUtils.isNotBlank(formToken); } /** * 生成一个新的表单,如果目前表单个数大于设定的最大表单数则先删除最早的一个表单。<br> * 新表单用RandomStringUtils.randomAlphanumeric(32)生成Token。 * * @return 创建的新表单 * @see #removeOldestForm(HttpServletRequest) * @see org.apache.commons.lang.RandomStringUtils#random(int) */ public Form newForm(HttpServletRequest request) { Form form = new Form(RandomStringUtils.randomAlphanumeric(32)); Map<String, Form> forms = getForms(request); synchronized (forms) { // 如果目前表单个数大于等于最大表单数,那么删除最老的表单,添加新表单。 if (forms.size() >= maxFormNum) { removeOldestForm(request); } forms.put(form.getToken(), form); } return form; } /** * 获得目前session中的表单列表。 * * @return 返回的Map中以表单的token为键,Form对象为值 */ @SuppressWarnings("unchecked") protected Map<String, Form> getForms(HttpServletRequest request) { Map<String, Form> formsInSession = null; HttpSession session = request.getSession(); synchronized (session) { formsInSession = (Map<String, Form>) session.getAttribute(SESSION_KEY_OF_FROMS); if (formsInSession == null) { formsInSession = new HashMap<String, Form>(); session.setAttribute(SESSION_KEY_OF_FROMS, formsInSession); } } return formsInSession; } /** * 删除最老的Form * * @see #destroyToken(HttpServletRequest, String) */ protected void removeOldestForm(HttpServletRequest request) { List<Form> forms = new ArrayList<Form>(getForms(request).values()); if (!forms.isEmpty()) { Form oldestForm = forms.get(0); for (Form form : forms) { if (form.getCreateTime().before(oldestForm.getCreateTime())) { oldestForm = form; } } destroyToken(request, oldestForm.getToken()); } } public void setMaxFormNum(int maxFormNum) { this.maxFormNum = maxFormNum; } }
Spring中的拦截器实现
package form; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; /** * 禁止表单重复提交拦截器 * * @author DigitalSonic */ public class DenyDuplicateFormSubmitInterceptor extends HandlerInterceptorAdapter { private FormManager formManager; public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { boolean flag = true; String token = request.getParameter(Form.FORM_UNIQ_ID_FIELD_NAME); if (token != null) { if (formManager.hasForm(request, token)) { formManager.destroyToken(request, token); } else { flag = false; throw new Exception("表单重复提交或过期,令牌[" + token + "]"); } } return flag; } public void setFormManager(FormManager formManager) { this.formManager = formManager; } }
在Spring MVC的HandlerMapping中配置该拦截器,随后在需要表单验证的Controller里做如下修改:
1、注入FormManager实例,主要是用newForm()生成一个新的Form对象
2、在返回的ModelAndView里加入该Form对象,假设名称是form
3、页面的表单中加入如下隐藏域
<input type="hidden" value="${form.token}" name="_form_uniq_id" />
代码中我去掉了些东西,应该还是能正常工作的,反正原理就是这么回事,呵呵。
发表评论
-
那些你该了解的Spring子项目
2013-03-20 14:39 1558去年年底,Spring Framework正式发布了3.2G ... -
《实战HotSpot JVM GC》分享Slides
2012-06-22 12:09 1823前阵子在QClub上海站做了一个与JVM GC优化相关的分享, ... -
如何简单模拟Web服务
2011-03-21 18:57 2254在SOA环境中,系统不可 ... -
代码中的坏味道
2011-01-07 04:09 1408最近InfoQ上连载了郑烨 ... -
isInfoEnabled究竟多有用?
2010-06-28 15:08 14584前段时间,公司里组织了一次代码检查,其中有一条检查项让我有些费 ... -
一个关于基于注解的Spring MVC的简单介绍
2009-05-26 17:52 2901前段时间给同事们做了一个关于Spring MVC的分享,简单介 ... -
实战Concurrent
2009-04-08 13:51 4048编写多线程的程序一直都是一件比较麻烦的事情,要考虑很多事情,处 ... -
Eclipse对JDK说“不”了
2008-09-30 21:21 2005最近开始翻译Spring Recipes了,既然是讲Sprin ... -
自己写的第一段AOP代码。
2005-04-13 22:36 1642代码1:使用安全的rand() ... -
FIFO、LRU、OPT的三个简单实现
2005-04-28 16:11 32401.利用随机数产生一个指令序列,共320条指令。其地址按下述原 ... -
JMeter小实验——JSP性能简单测试
2006-04-17 16:56 2497首先要做的当然是到Apache的站点下在一个最 ... -
利用缓存提高小型站点性能
2006-04-23 16:11 1256最近结束了 ... -
都是JDBC-ODBC惹得祸
2006-09-23 11:23 1226前阵子的一个项目需要 ... -
Fedora下瞎折腾了一个半小时,还是Sun的虚拟机可靠
2007-03-23 22:00 1236今天早上跑去了网络学院做技术支持, 他们碰到的问题是一个web ... -
一个泛型Hibernate DAO实现
2007-08-06 11:58 1792自己四处参考,写写抄抄折腾出来的一个泛型Hibernate D ... -
自己出的几道关于Spring和Hibernate的面试题
2008-02-29 16:49 2531很简单的题目,随便出着玩的,如果你是高手就请直接跳过,若有雷同 ... -
Spring MVC快速上手教程
2008-04-01 22:00 3129Spring Framework可以被使用在很多场合之中,考虑 ... -
一些关于Liferay的使用心得
2008-01-07 15:58 2195Liferay是一个出色的Java开源Portal产品,其中整 ...
相关推荐
在Web开发中,表单重复提交是一个常见的问题,可能导致数据的冗余或错误。JavaScript作为客户端脚本语言,可以通过多种策略来防止用户意外或恶意地多次提交表单。以下是一些关键的知识点: 1. **禁用提交按钮**:最...
在使用Layui设计表单时,经常会遇到表单提交后页面刷新导致的重复提交问题。为了解决这个问题,开发者需要确保表单只提交一次,防止不必要的数据重复处理或者服务器的重复计算。下面详细解释几种防止Layui表单重复...
在现代Web应用开发中,防止表单重复提交是一项重要的任务,因为这可能导致数据不一致性和服务器资源浪费。本文将深入探讨如何使用Spring Boot 2.1、Redis和拦截器来实现这一功能。以下是对这个主题的详细解释: ...
在ASP.NET web应用程序中,表单重复提交是一个常见的问题,可能导致数据不一致或者数据库操作的冗余。为了确保用户在提交表单时不会无意或有意地多次发送请求,我们需要实施有效的防止表单重复提交的策略。以下是...
在IT行业中,表单防重复提交是一个常见的需求,特别是在高并发和数据一致性要求较高的场景下。本项目"基于注解+redis实现表单防重复提交.zip"提供了一种利用SpringBoot框架来解决这一问题的方法。这里我们将深入探讨...
Spring Boot 防止重复提交是指在用户提交表单或请求时,防止同一客户端在短时间内对同一 URL 的重复提交,从而避免服务器端的处理压力和数据的一致性问题。下面将详细介绍 Spring Boot 防止重复提交实现方法的相关...
在处理用户交互时,尤其是表单提交,一个常见的问题是防止表单的重复提交。这可能导致数据的不一致性和其他问题。本文将深入探讨如何在Struts框架中利用Token机制来解决这个问题。 一、表单重复提交问题 表单重复...
在IT领域,尤其是在Web开发中,表单重复提交是一个常见的问题,可能导致数据不一致或数据库冗余。本文将深入探讨如何在客户端和服务器端通过不同的方法来防止这种情况发生。 首先,客户端的防护主要依赖于...
在IT行业中,尤其是在前端开发领域,防止用户重复提交是一项重要的任务,这有助于避免因网络延迟或用户误操作导致的数据混乱。Element-UI,一个流行的Vue.js组件库,提供了多种方式来处理这个问题。以下是如何在使用...
在IT行业中,尤其是在Web开发领域,防止用户多次重复提交数据是一项重要的任务,这可以避免数据库出现冗余数据,保持系统稳定。"修改禁止多次重复提交"这个话题涉及到前端交互、后端处理以及数据库操作等多个层面。...
例如,在用户提交表单后,由于网络延迟或其他原因导致用户多次点击提交按钮或浏览器缓存导致页面刷新时,可能会出现数据的重复提交。这不仅会导致数据库数据不一致的问题,还可能引发业务逻辑错误,如订单重复等。...
提交表单后提交禁用提交按钮,防止重复提交.
好友使用vue技术封装了一个专门用于提交表单和下载文件的“防抖按钮”,其实现原理和使用方法看这里 https://blog.csdn.net/PursueExcellence/article/details/103903139。
首先,关于表单重复提交的原因,有以下几点: 1. 服务器或网络延迟导致用户多次点击提交按钮。 2. 用户在表单提交后刷新浏览器页面。 对于第一种原因,即多次点击提交按钮导致的重复提交,Struts2框架提供了token...
在Web开发中,尤其是使用JavaServer Pages (JSP) 技术时,页面表单的重复提交是一个常见的问题。这可能会导致数据不一致或者服务端处理逻辑错误。本篇文章将探讨如何有效地防止JSP页面中的表单重复提交,确保系统的...
在Web开发中,用户表单重复提交是一个常见的问题,它可能导致数据冗余或者数据库状态的不一致。本示例提供了一种解决方案,包括在前端JavaScript和后端进行处理,以防止用户因网络延迟或误操作导致的多次提交。 ...
表单重复提交是指在一次请求完成之前防止重复提交,解决表单重复提交有多种形式,以下以 Aop+自定义注解+Redis 为例来介绍。 解决方案的详细流程 1. 当页面加载时,前端请求后台,后台生成 token 缓存到 Redis ...
然而,如果没有适当处理,可能会出现表单重复提交的问题,这可能导致数据的不一致性和服务器资源的浪费。本篇文章将深入探讨如何防止表单重复提交,主要关注于基于Struts2框架的解决方案。 首先,理解表单重复提交...