防止用户重复提交表单

      由于没有用jsp,所以在这个实例中用一个servlet来输出表单页面,另一个servlet用来处理表单。具体思路在程序中写出:

/*
 * 防止表单重复提交
 * 思路:程序在生成表单的时候带个随机数,在服务器端检查表单带的随机数与服务器中的随机数是否一致
 * 在客户端也可以javascript代码来阻止
 * 客户端阻止会减小服务器的压力,不过在开发的时候,客户端和服务器端都需要进行阻止
 * 在服务器端阻止的话,该表单需要由某个程序生成,这里用servlet来代替
 */
public class FormServlet extends HttpServlet {
 
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        
        String token = TokenProcessor.getInstance().makeToken();//获得随机数
        request.getSession().setAttribute("token", token);//在服务器端保存随机数
        
        //产生表单
        out.println("<form action='/test/servlet/DoFormServlet' method='post'>");
            out.println("<input type='hidden' name='token' value='" + token + "'/>");//通过隐藏字段带一个随机数给服务器
            out.println("用户名:<input type='text' name='username'/>");
            out.println("<input type='submit' value='提交'/>");
        out.println("</form>");
    }
 
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
 
        doGet(request, response);
    }
}
 
class TokenProcessor {
    
    /*
     * 随机数应该要保证唯一性,所以最好用一个对象来生成。所以该类需要设计成单例的。
     * 单例设计模式:(保证类的对象在内存中只有一个)
     * 1. 把类的构造函数私有
     * 2. 自己创建一个类的对象
     * 3. 对外提供一个公共方法,返回类的对象
     */
    private TokenProcessor(){}
    private static final TokenProcessor instance = new TokenProcessor();
    
    public static TokenProcessor getInstance() {
        return instance;
    }
    
    //产生一个随机数
    public String makeToken() {        
        String token = (System.currentTimeMillis() + new Random().nextInt(9999999)) + "";        
        /*
         * 数据指纹:不管数据多大,得到的数据指纹都是相同大小,128位长-16字节
         * 拿到数据指纹再反推原始数据是不可能的。
         * 我们可以使用md5算法可以得到数据指纹。
         */
        try {
            MessageDigest md = MessageDigest.getInstance("md5");//获得md5算法
            byte md5[] = md.digest(token.getBytes());//将上面的token转成字节后,获得其指纹,存到字节数组中
            /*
             * 不能像下面这样return,可能会出现乱码
             * 因为md5中保存的是字节,new String(md5)的时候会将字节转成字符串,这样就必然要去查码表,如果没指定就查gb2312
             * 如果相应的字节在码表中没有那就出现乱码
             */
            //return new String(md5);
            
            /*
             * base64编码:将任意二进制编码成明文字符。
             * 如:把3字节变成4字节,并且把每一个字节的数据变成一个字符串
             * 假设有三字节数据: 01010000    01010011    01100101  总共24位
             * 变成4字节的话,每个字节存储该数据的6位,剩下2位补0,
             * 所以base64编码后的数据为: 00 010100 00 00 0101 00 0011 01 00  100101
             * 编码后的数据,最小值为0,最大值为63,共有64个数据,故称为base64编码
             * 在0-63之间都有字符与之对应,这些字符都是键盘上可以看到的某个字符。
             */
            BASE64Encoder encoder = new BASE64Encoder();//第一次使用的时候可能无法import包,解决方案如下:
            /*
             * 解决方案1(推荐):
             *     只需要在project build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。
             * 解决方案2:
             *     Windows -> Preferences -> Java -> Compiler -> Errors/Warnings ->
             *     Deprecated and trstricted API -> Forbidden reference (access rules): -> 改为warning
             */
            return encoder.encode(md5);
            
            
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);//转成运行时异常,这样上面就不需要处理了
            //一般情况下,都是将异常转成运行时异常往上抛;
            //有一种情况:希望上面处理这个异常,那么我们抛编译时异常。
        }
    }     
}

用户提交后,会交给DoFormServlet去处理,我们看一下DoFormServlet.java:

//处理表单数据
public class DoFormServlet extends HttpServlet {
 
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
 
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        
        boolean b = isToken(request); //
        if(b == false) {
            System.out.println("请不要重复提交!");
            return;
        }
        
        //处理提交请求之前,需要先将session中保存的随机数移除掉
        request.getSession().removeAttribute("token");        
        System.out.println("处理用户提交请求!");
    }
 
    private boolean isToken(HttpServletRequest request) {        
        String clientToken = request.getParameter("token");//客户端token
        if(clientToken == null)
            return false;
        String serverToken = (String)request.getSession().getAttribute("token");//服务器端token
        if(serverToken == null)//如果已经提交过一次了,服务器会把随机数移除掉,serverToken就变成null了
            return false;        
        if(!clientToken.equals(serverToken))
            return false;    
        return true;
    }
 
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
 
        doGet(request, response);
    }
 
}

过程是这样的,用户在登陆之前,服务器端会先生成一个token,然后将这个token存到session中,并将其作为隐藏字段打给浏览器让用户登陆,很明显,用户登陆后就会将这个token带过来,服务器端处理请求前会先判断客户端带过来的token和session中保存的是否一样,是的话将session中的token删掉,然后处理请求,这时候session中已经没有这个token了,当用户回退再次提交时,服务器端又会判断,这时候就会返回false了,就提示用户不要重复提交。

$.ajax请求中有一个beforeSend方法,用于在向服务器发送请求前执行一些动作。

$.ajax({
    beforeSend: function(){
     // Handle the beforeSend event
    },
    complete: function(){
     // Handle the complete event
    }
    // ......
});

防止重复数据

在实际项目开发中,提交表单时常常由于网络或者其原因,用户点击提交按钮误认为自己没有操作成功,进而会重复提交按钮操作次数,如果页面前端代码没有做一些相应的处理,通常会导致多条同样的数据插入数据库,导致脏数据的增加。要避免这种现象,在$.ajax请求中的beforeSend方法中把提交按钮禁用掉,等到Ajax请求执行完毕,在恢复按钮的可用状态。

// 提交表单数据到后台处理
$.ajax({
    type: "post",
    data: studentInfo,
    contentType: "application/json",
    url: "/Home/Submit",
    beforeSend: function () {
        // 禁用按钮防止重复提交
        $("#submit").attr({ disabled: "disabled" });
    },
    success: function (data) {
        if (data == "Success") {
            //清空输入框
            clearBox();
        }
    },
    complete: function () {
        $("#submit").removeAttr("disabled");
    },
    error: function (data) {
        console.info("error: " + data.responseText);
    }
});

ajax请求服务器加载数据列表时提示loading(“加载中,请稍后...”),

$.ajax({
    type: "post",
    contentType: "application/json",
    url: "/Home/GetList",
    beforeSend: function () {
        $("loading").show();
    },
    success: function (data) {
        if (data == "Success") {
            // ...
        }
    },
    complete: function () {
        $("loading").hide();
    },
    error: function (data) {
        console.info("error: " + data.responseText);
    }
});

 

<script Language='JavaScript'> 
    function formsubmit() { 
        Today = new Date(); 
        var NowHour = Today.getHours(); 
        var NowMinute = Today.getMinutes(); 
        var NowSecond = Today.getSeconds(); 
        var mysec = (NowHour*3600)+    (NowMinute*60)+NowSecond; 
    if((mysec-document.formsubmitf.mypretime.value)>600) 
    //600只是一个时间值,就是5分钟内禁止重复提交,值随你高兴设 
    { 
      document.formsubmitf.mypretime.value=mysec; 
    } 
    else 
    { 
      alert(' 按一次就够了,请勿重复提交!请耐心等待!谢谢合作!'); 
      return false; 
    } 
      document.forms.formsubmitf.submit(); 
 } 
</script> 

                                

 

参考:

https://blog.csdn.net/eson_15/article/details/51262736

https://blog.csdn.net/mggwct/article/details/52153591

 

posted on 2018-10-17 12:07  溪水静幽  阅读(178)  评论(0)    收藏  举报