防止重复提交解决方案

防止重复提交解决方案
B/S结构的软件开发中,特别是在越大型的分布式应用中体现的越明显,后端的处理往往会因为出现较多的时间消耗而引起延迟,这种延迟有可能过长而最终使用户认为是自己的操作错误,导致他们重新提交请求,由于任务的重复提交,服务器资源大部分被占用,情节严重可能出现类似死机现象。

预期达到目标:
    1、当用户进行的是Refresh/Reload/Back/Forward操作、以及先Back再Submit操作时,仅仅是reloading先前的结果页。
    2、当用户重复提交同一个任务操作时,后台服务接收并处理第一次提交的任务,后面提交不起作用(不转向也不提示)。
    3、该功能具有公用性。

基本形成思路:
    1、在basic filter中实现公用性
        if(true){//问题1:如何确定是否为重复提交
            ...
            chain.doFilter(request,response);
        }else{
            //问题2:如何实现不转向、不提示也不显示空白页
        }
    
    2、网上资料概括
        a、提交表单后按钮变灰/隐藏提交按钮
        b、在js里设置全局变量,提交后修改该变量的值,依据变量的值判断是否重复提交
        var flag=true;
        function checkForm(){
            if (flag==false){
                return;
            }
            flag=false;
            document.form1.submit();                      
        }
        c、struts (webwork没有找到这个资料)
        验证事务控制令牌,<html:form >会自动根据session中标识生成一个隐含input代表令牌,防止两次提交
        在action中:                       
        //<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae">                       
        if (!isTokenValid(request))
            errors.add(ActionErrors.GLOBAL_ERROR,new ActionError("error.transaction.token"));
        resetToken(request); //删除session中的令牌
        action有这样的一个方法生成令牌华
        protected String generateToken(HttpServletRequest request) {
            HttpSession session = request.getSession();
            try {
                byte id[] = session.getId().getBytes();
                byte now[] = new Long(System.currentTimeMillis()).toString().getBytes();
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.update(id);
                md.update(now);
                return (toHex(md.digest()));
            } catch (IllegalStateException e) {
                return (null);
            } catch (NoSuchAlgorithmException e) {
                return (null);
            }
        }  
                    
        d、用户使用浏览器时,可以经常使用向后的按钮,因此就有可能重复提交一个他们已经提交过的form,这样就会带来一个重复事务处理的问题。同样,一个用户也可能在接收到一个确认的页面之前按下停止的按钮,接着再次提交同一个form。对于这些情况,我们都想跟踪并且禁止这些重复的提交,我们可以使用一个控制servlet来提供一个控制点,以解决这个问题。
        同步记号(Synchronizer (or Dvu) Token)
        这个策略是为了解决重复的form提交问题。一个同步的记号被设置在一个用户的Session中,并且包含在返回到客户的每一个form中。当form 被提交时,form中的同步标记就和Session中的同步标记作对比。在form首次提交的时候,这两个标记应该是一样的。如果标记不一样,那么该 form就会禁止提交,一个错误就会返回给用户。在用户提交一个form时,如果按下浏览器中的后退按钮并尝试重新提交同一个form时,标记就会出现不匹配的现象。
        另一方面,如果两个标记值匹配,那么我们就可以确信整个流程是正确的。在这种情况下,Session中的标记值就会被修改为一个新的值,同时允许提交该form。
        你也可以使用这个策略来控制对某些页面的直接访问,就好象上面资源保护中描述的一样。例如,假设一个用户将某个应用的页面A收藏到收藏夹中,而页面A只允许通过页面B和C访问。当用户直接通过收藏夹来访问页面A,这时页面的访问顺序就是不正确的,这样同步标记将处在一个不同步的状态,或者它根本就不存在。不论怎样,访问都被禁止了。
    
        e、做一个hidden框,名字自己定,提交后得到这个值放入session,提交前判断session是否为空。
        f、另Struts提供利用Token防止重复提交的方法:
        用户请求录入页面,这个与服务器建立的一次连接过程中,在服务器端①【生成一个session标识,同时返回到客户端一个与此匹配的hidden域】。用户提交了此页面,服务器端首先②【判断此hidden域与session标识是否匹配】,若不匹配,终止保存操作,提示同一表单不能提交两次,同时①【新建一个session标识和hidden域】,返回录入页面;若匹配,执行插入保存操作,同时③【清空(重置reset)session标识】。
        Struts正在基于这样的思路在org.apache.struts.action.Action类中提供了内置支持方法:
        java代码:
                
            protected void saveToken(HttpServletRequest request) 配合标签对应于①
            protected boolean isTokenValid(HttpServletRequest request) 对应于②
            protected void resetToken(HttpServletRequest request) 对应于③
                
        这样我们在写程序的时候,结合Struts的html标签,只要
            1.在forward到insert.jsp页面前加一个action执行saveToken(request)操作,或干脆在insert.jsp中写
            2.保存前加个判断操作isTokenValid(request)
            3.若isTokenValid(request)返回false,执行saveToken(request)操作,返回错误提示页面;true则执行resetToken(request)操作,然后进行实际的保存操作

解决方案:
   1、后台公共类中实现前台的Form中自动生成两个hidden文本功能,一个是作page是否重复提交判断,并由系统自动附上关键值(如struts采用的方案);另一个作为button是否重复提交判断(struts中好像没有)。由后台公共类实现界面两个hidden text自动生成的好处在于公用性。
   2、在basic filter中根据两个hidden text值判断是否为重复提交。
   3、javascript中作一个公共方法,实现功能:如果需要判断是否重复提交,就给第二个hidden text附上关键值,并使该功能不可用。

你可能感兴趣的:(防止重复提交解决方案)