spring mvc重复提交解决办法

转载 2012年03月28日 10:41:20

此方法只适合.单方法(SimpleFormController  ).不适合多方法

 

public   class   BaseFormController   extends   SimpleFormController   {

/**

  *   防止多次提交

  *   

  *   @param   request

  *   @param   response

  *   @return

  *   @throws   Exception

  */

protected   ModelAndView   disallowDuplicateFormSubmission(HttpServletRequest   request,   HttpServletResponse   response)   throws   Exception   {

BindException   errors   =   new   BindException(formBackingObject(request),   getCommandName());

errors.reject("duplicateFormSubmission",   null,   "对不起,你不能重复提交同一表单内容!");

return   showForm(request,   response,   errors);

}

/*

  *   (non-Javadoc)

  *   

  *   @see   org.springframework.web.servlet.mvc.AbstractFormController#handleInvalidSubmit(javax.servlet.http.HttpServletRequest,

  *             javax.servlet.http.HttpServletResponse)

  */

protected   ModelAndView   handleInvalidSubmit(HttpServletRequest   request,   HttpServletResponse   response)   throws   Exception   {

return   disallowDuplicateFormSubmission(request,   response);

}

}  


关于表单防重复提交一些东东 .
分类: Java相关 2009-03-13 22:33 2122人阅读 评论(3) 收藏 举报 
前阵子弄了些表单防重复提交的东西,想整理整理,免得下次要用时再四处去找,其实这里的东西还是挺简单的。

原理:

在Session中保存一个表单的唯一编号,将该编号放在一个隐藏域中,同其他数据一同提交。在提交表单后,通过拦截器或其他机制检查唯一编号,如果存在则说明表单是第一次提交,如果不存在则被重复提交(理由很简单,在第一次提交检查后就会从Session中移除该编号)。保存编号可以用一个 HashMap。

上代码:

表单类,用于保存表单创建时间和表单的标示

[java] view plaincopyprint?
01.package form;  
02.import java.io.Serializable;  
03.import java.util.Date;  
04.import java.util.HashMap;  
05.import java.util.Map;  
06.import org.apache.commons.lang.builder.ToStringBuilder;  
07./** 
08. * 表单类 
09. * 
10. * @author DigitalSonic 
11. */  
12.public class Form implements Serializable {  
13.    /** 
14.     * serialVersionUID 
15.     */  
16.    private static final long   serialVersionUID        = 8796758608626021150L; 
17.    public static final String  FORM_UNIQ_ID_FIELD_NAME = "_form_uniq_id";  
18.    /** 表单标识*/  
19.    private String              token;  
20.    /** 表单创建时间*/  
21.    private Date                createTime;  
22.    /** 
23.     * 构造函数 
24.     */  
25.    public Form(String token) {  
26.        this.token = token;  
27.        this.createTime = new Date();  
28.    }  
29.    public String toString() {  
30.        return ToStringBuilder.reflectionToString(this);  
31.    }  
32.    public String getToken() {  
33.        return token;  
34.    }  
35.    public Date getCreateTime() {  
36.        return createTime;  
37.    }  
38.}  
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;
    }
}

表单管理器接口

[java] view plaincopyprint?
01.package form;  
02.import javax.servlet.http.HttpServletRequest;  
03./** 
04. * 表单管理器,负责管理Session中的表单。 
05. * 
06. * @author DigitalSonic 
07. */  
08.public interface FormManager {  
09.    /** 
10.     * 生成一个新的表单 
11.     */  
12.    public Form newForm(HttpServletRequest request);  
13.    /** 
14.     * 判断表单是否存在。 
15.     */  
16.    public boolean hasForm(HttpServletRequest request, String token);  
17.    /** 
18.     * 访问参数中是否存在表单Token。 
19.     */  
20.    public boolean hasFormToken(HttpServletRequest request);  
21.    /** 
22.     * 销毁一个表单 
23.     */  
24.    public void destroyToken(HttpServletRequest request, String token);  
25.    /** 
26.     * 打印表单信息。 
27.     */  
28.    public String dumpForm(HttpServletRequest request, String token);  
29.}  
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);
}


表单管理器接口实现

[java] view plaincopyprint?
01.package form;  
02.import java.util.ArrayList;  
03.import java.util.HashMap;  
04.import java.util.List;  
05.import java.util.Map;  
06.import javax.servlet.http.HttpServletRequest;  
07.import javax.servlet.http.HttpSession;  
08.import org.apache.commons.lang.RandomStringUtils;  
09.import org.apache.commons.lang.StringUtils  
10./** 
11. * 表单管理器实现类 
12. * 
13. * @author DigitalSonic 
14. */  
15.public class FormManagerImpl implements FormManager {  
16.    private static final String SESSION_KEY_OF_FROMS = "_forms_in_session";  
17.    /** 表单最大个数 */  
18.    private int                 maxFormNum           = 7;  
19.      
20.    /** 
21.     * 销毁一个表单 
22.     */  
23.    public void destroyToken(HttpServletRequest request, String token) {  
24.        getForms(request).remove(token);  
25.    }  
26.    /** 
27.     * 打印表单信息。 
28.     */  
29.    public String dumpForm(HttpServletRequest request, String token) {  
30.        Form form = getForms(request).get(token);  
31.        if (form == null) {  
32.            return "null";  
33.        }  
34.        return form.toString();  
35.    }  
36.    /** 
37.     * 判断表单是否存在。如果token为null,直接返回false。 
38.     * 
39.     * @see #getForms(HttpServletRequest) 
40.     */  
41.    public boolean hasForm(HttpServletRequest request, String token) {  
42.        if (token == null) {  
43.            return false;  
44.        }  
45.        return getForms(request).containsKey(token);  
46.    }  
47.      
48.    /** 
49.     * 访问参数中是否存在表单Token。 
50.     */  
51.    public boolean hasFormToken(HttpServletRequest request) {  
52.        String formToken = request.getParameter(Form.FORM_TOKEN_FIELD_NAME);  
53.        return StringUtils.isNotBlank(formToken);  
54.    }  
55.    /** 
56.     * 生成一个新的表单,如果目前表单个数大于设定的最大表单数则先删除最早的一个表单。<br> 
57.     * 新表单用RandomStringUtils.randomAlphanumeric(32)生成Token。 
58.     * 
59.     * @return 创建的新表单 
60.     * @see #removeOldestForm(HttpServletRequest) 
61.     * @see org.apache.commons.lang.RandomStringUtils#random(int) 
62.     */  
63.    public Form newForm(HttpServletRequest request) {  
64.        Form form = new Form(RandomStringUtils.randomAlphanumeric(32));  
65.        Map<String, Form> forms = getForms(request);  
66.        synchronized (forms) {  
67.            // 如果目前表单个数大于等于最大表单数,那么删除最老的表单,添加新表单。   
68.            if (forms.size() >= maxFormNum) {  
69.                removeOldestForm(request);  
70.            }  
71.            forms.put(form.getToken(), form);  
72.        }  
73.        return form;  
74.    }  
75.    /** 
76.     * 获得目前session中的表单列表。 
77.     * 
78.     * @return 返回的Map中以表单的token为键,Form对象为值 
79.     */  
80.    @SuppressWarnings("unchecked")  
81.    protected Map<String, Form> getForms(HttpServletRequest request) {  
82.        Map<String, Form> formsInSession = null;  
83.        HttpSession session = request.getSession();  
84.        synchronized (session) {  
85.            formsInSession = (Map<String, Form>) session.getAttribute(SESSION_KEY_OF_FROMS); 
86.            if (formsInSession == null) {  
87.                formsInSession = new HashMap<String, Form>();  
88.                session.setAttribute(SESSION_KEY_OF_FROMS, formsInSession);  
89.            }  
90.        }  
91.        return formsInSession;  
92.    }  
93.    /** 
94.     * 删除最老的Form 
95.     * 
96.     * @see #destroyToken(HttpServletRequest, String) 
97.     */  
98.    protected void removeOldestForm(HttpServletRequest request) {  
99.        List<Form> forms = new ArrayList<Form>(getForms(request).values());  
100.        if (!forms.isEmpty()) {  
101.            Form oldestForm = forms.get(0);  
102.            for (Form form : forms) {  
103.                if (form.getCreateTime().before(oldestForm.getCreateTime())) { 
104.                    oldestForm = form;  
105.                }  
106.            }  
107.            destroyToken(request, oldestForm.getToken());  
108.        }  
109.    }  
110.    public void setMaxFormNum(int maxFormNum) {  
111.        this.maxFormNum = maxFormNum;  
112.    }  
113.}  
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中的拦截器实现

[java] view plaincopyprint?
01.package form;  
02.import javax.servlet.http.HttpServletRequest;  
03.import javax.servlet.http.HttpServletResponse;  
04.import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
05./** 
06. * 禁止表单重复提交拦截器 
07. * 
08. * @author DigitalSonic 
09. */  
10.public class DenyDuplicateFormSubmitInterceptor extends HandlerInterceptorAdapter { 
11.    private FormManager formManager;  
12.      
13.    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
14.                             Object handler) throws Exception {  
15.        boolean flag = true;  
16.        String token = request.getParameter(Form.FORM_UNIQ_ID_FIELD_NAME);  
17.        if (token != null) {  
18.            if (formManager.hasForm(request, token)) {  
19.                formManager.destroyToken(request, token);  
20.            } else {  
21.                flag = false;  
22.                throw new Exception("表单重复提交或过期,令牌[" + token + "]");  
23.            }  
24.        }  
25.        return flag;  
26.    }  
27.    public void setFormManager(FormManager formManager) {  
28.        this.formManager = formManager;  
29.    }  
30.}  
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" />  
<input type="hidden" value="${form.token}" name="_form_uniq_id" />

代码中我去掉了些东西,应该还是能正常工作的,反正原理就是这么回事,呵呵。

抱歉打扰一下,我想说的是:
第二.你不必加同步锁,Session每个线程各有一份
第三.Map的value目的只是为了提供date,好替除最久未使用的token,但你不必for循环每一项吧,token每次put到map是经过你的代码的对吗?那么你很轻松就可记录下put的顺序。还好这个map不太大,但是你的设计可以更好的啊。

建议:把map打散,LinkedList记录顺序,HashSet快速contain到key。
TreeMap按date排序,基本上能解决这个问题


Spring MVC如何防止重复提交?类似Struts Token机制!
2007-03-01 18:32

首先,需要将继承了SimpleFormController之类的sessionForm设为true。这样,在显示一个新表单时,Spring会将command存放在session中,而在提交表单时,Spring会从session中取出此command,随后立即从session中删除存放command的attribute。如果发现在session中没有command,Spring将其断定为重复提交,转而执行handleInvalidSubmit(request, response),可覆盖此方法负责防止重复提交的任务。可以这么说,当setSessionForm(true)之后,如果没有先后经历显示表单、提交表单的过程,就会被认为是重复提交表单。而有一些情况下却必须重复提交表单,如,修改数据库的数据后,试图写入数据库时因某些异常失败,如果此时异常被当前页面捕获并依旧返回当前页面,由于command已经被Spring在后台从session中移走,因此,就被认为是无效重复提交,从而导致第二次经修改后的记录无法正确提交到数据库中。handleInvalidSubmit()必须考虑到这种情况。

-------------------------------------------------------------------------------------------------

方法2

楼上提供的方法比较麻烦。
我在项目中使用过一种比较简单的方法,就是使用一个Filter,设置request的参数,使禁止Browser对页面进行缓存,通过这种变通的方式来解决。

SpringMVC表单防止重复提交

利用SpringMVC的注解加拦截器完成。实现如下: 1、首先创建注解token: @Target(ElementType.METHOD) @Retention(RetentionPoli...

高并发下重复提交问题的处理

高并发下重复提交问题的解决方案

springmvc下的基于token的防重复提交

问题描述: 现在的

springmvc 拦截器 防止重复提交表单

第一种方法:判断session中保存的token 比较麻烦,每次在提交表单时都必须传入上次的token。而且当一个页面使用ajax时,多个表单提交就会有问题。 注解Token代码: [...

SpringMVC拦截器实现防重复提交

防重复提交在前端和后端都需要控制:   前端:点击按钮后将按钮置灰不可用   后端:采用自定义拦截器的方式,模拟struts2自带的token拦截器实现原理 自定义拦截器实现以下防重原理: 1.初始...

Spring MVC拦截器+注解方式实现防止表单重复提交

转载自https://m.oschina.net/blog/191666 Spring MVC拦截器+注解方式实现防止表单重复提交 原理:在新建页面中Session保存token随机码,当保存时验...

spring mvc重复提交解决办法

http://blog.csdn.net/dingx/article/details/3988908 此方法只适合.单方法(SimpleFormController  ).不适合多方法   pu...

spring mvc重复提交解决办法

此方法只适合.单方法(SimpleFormController  ).不适合多方法   public   class   BaseFormController   extends   Simple...
  • heLove
  • heLove
  • 2013年11月19日 16:58
  • 2074

spring mvc重复提交解决办法

此方法只适合.单方法(SimpleFormController  ).不适合多方法 public   class   BaseFormController   extends   Sim...
  • viproc
  • viproc
  • 2012年04月25日 10:06
  • 747

Spring MVC @Transactional注解方式事务失效的解决办法

转载自:http://blog.csdn.net/z69183787/article/details/37819831 前文提到,最新换了框架,新项目用SpringMVC + spring Jd...
  • lkx94
  • lkx94
  • 2017年03月20日 15:34
  • 184
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:spring mvc重复提交解决办法
举报原因:
原因补充:

(最多只允许输入30个字)