spring mvc重复提交解决办法

此方法只适合.单方法(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对页面进行缓存,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值