当前位置: 首页 > news >正文

JavaWeb使用Session防止表单重复提交

在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交。

 

1.什么是表单重复提交

 

> 在不刷新表单页面的前提下:
        >> 多次点击提交按钮
        >> 已经提交成功, 按 "回退" 之后, 再点击 "提交按钮".
        >> 在控制器响应页面的形式为转发情况下,若已经提交成功, 然后点击 "刷新(F5)"

 

> 注意:
        >> 若刷新表单页面, 再提交表单不算重复提交
        >> 若使用的是 redirect 的响应类型(地址栏发生变化), 已经提交成功后, 再点击 "刷新", 不是表单的重复提交

 

2、客户端利用JavaScript防止表单重复提交

既然存在上述所说的表单重复提交问题,那么我们就要想办法解决,比较常用的方法是采用JavaScript来防止表单重复提交,具体做法如下:

修改form.jsp页面,添加如下的JavaScript代码来防止表单重复提交

 

 

 

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>首页</title>
<script type="text/javascript">
/************客户端JS防止重复提交表单方法一
 
 var isSubmit=false;
function dosubmit(){
if(!isSubmit){    isSubmit=true;
                  return true;}
else{return false;}
    
}
************/
//第二中方法设置表单的提交按钮点击一次后不能点击,document后面没括号,刷新跟后退同样可以重复提交。
function dosubmit(){
    
    document.getElementById("ss").disabled='disabled';
    alert("HH");
    return true;
}
</script>
</head>
<body>
<form action="/Servlet/lOGIN1"  method="post" onsubmit="return dosubmit()">
用户名 :<input type="text" name="name"><br/><br/>
<input id="ss" type="submit" value="提交" >
</form>
</body>
</html>

 

我们看看使用了JavaScript来防止表单提交重复是否可以成功,运行效果如下:

 

后退之后按钮不能点击

另外还有一种做法就是提交表单后,将提交按钮隐藏起来,这种做法和将提交按钮设置为不可用是差不多的,个人觉得将提交按钮隐藏影响到页面布局的美观,并且 可能会让用户误以为是bug(怎么我一点击按钮,按钮就不见了呢?用户可能会有这样的疑问),我个人在开发中用得比较多的是表单提交后,将提交按钮设置为 不可用,反正使用JavaScript防止表单重复提交的做法都是差不多的,目的都是让表单只能提交一次,这样就可以做到表单不重复提交了

三、利用Session防止表单重复提交(常用)

在服务器端解决,在服务器端解决就需要用到session了。

具体的做法:在服务器端生成一个唯一的随机标识号,专业术语称为 Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏 域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的 Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的 Session域中存储的标识号。
  在下列情况下,服务器程序将拒绝处理用户提交的表单请求:

    1. 存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
    2. 当前用户的Session中不存在Token(令牌)
    3. 用户提交的表单数据中没有Token(令牌)

 

1.产生随机数(令牌)跳转到表单页面的Java

package Session;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import sun.misc.BASE64Encoder;

@WebServlet("/ServletForm_2")
public class ServletForm_2 extends HttpServlet {
    private static final long serialVersionUID = 1L;
 
    public ServletForm_2() {
        super();
        
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        TokenProcessor tp=TokenProcessor.getTp();
        String rand=tp.getToken();
        HttpSession session=request.getSession();
        session.setAttribute("rand", rand);   //用session将数据带过去
        request.getRequestDispatcher("/SecondForm.jsp").forward(request,response);;
                
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        doGet(request, response);
    }

}


//令牌处理器,产生一个随机数,单利模式,一个对象产生
class TokenProcessor{
    //单利模式
    /***
     * 1.构造方法私有
     * 2.创建一个对象
       3.公开一份方法暴露对象
     */
    private  TokenProcessor() {
        
    }

        private static TokenProcessor tp=new TokenProcessor();

    public static TokenProcessor getTp(){
        return tp;
    }
    
    public String getToken(){
        //token是系统当前时间毫秒数+随机数变为的字符串。长度不同
        String token=System.currentTimeMillis()+new Random().nextInt()+"";
        //利用MD5摘要算法得到固定长度的字符串
        try {
            MessageDigest md=MessageDigest.getInstance("md5");
            //根据MD5算法得到数据的指纹
            byte[] md5=md.digest(token.getBytes());
            //BASE64编码,3BYTE变为4byte(每6位前面加2零)
            BASE64Encoder encoder=new BASE64Encoder();
            String ss=encoder.encode(md5);
            return ss;
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            throw new RuntimeException(e); 
        }
    }
    
    
}

 

表单JSP:

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>

<form action="/Servlet/lOGIN2" method="post">
用户名:<input type="text"><br/><br/>
密码:<input type="password"><br/>
<input type="hidden"  name="hid" value="<%= request.getSession().getAttribute("rand") %>">
<input type="submit" value="提交">



</form>



</body>
</html>

 

处理表单的Servlet

package Session;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Servlet implementation class lOGIN2
 */
@WebServlet("/lOGIN2")
public class lOGIN2 extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public lOGIN2() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //判断带过的随机数是否有效
        boolean isValied=isTokenValid(request);
        
        if(!isValied){System.out.println("无效,请不要重复提交!"); return ;}
        
        
        request.getSession().removeAttribute("rand");
        System.out.println("正在提交。。。。。。。。。");
        
        
        
    }

    private boolean isTokenValid(HttpServletRequest request) {
    

    //服务器端带随机数
    String ser_hid=(String) request.getSession().getAttribute("rand");
    
    //客户端带过来带随机数
    String cli_hid=request.getParameter("hid");
    System.out.println(ser_hid+"         "+cli_hid+"    5");
        if(cli_hid==null){return false;}
        if(ser_hid==null){return false;}
        if(!cli_hid.equals(ser_hid)){return false;}
        return true;
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}

 

结果:  http://localhost:8080/Servlet/ServletForm_2

查看页面源码:

 

第一次提交:

 

刷新页面:

 

后退后提交:

 

 

 

********: struts的防止表单重复提交比较简单,参考:  http://www.cnblogs.com/qlqwjy/p/7190272.html

 

相关文章:

  • redis高级(分布式缓存实现,spring integration)
  • iOS 参考 网络书籍
  • react redux 登陆拦截
  • 细谈多个平台编程与网页设计切换启示录----my note
  • elasticsearch 性能监控基础
  • 企业内部DNS从服务器架构的步骤
  • select a method for export 选项
  • 使用JNI与原生代码的通信
  • Yii源码解读-服务定位器(Service Locator)
  • JAVA-JSP之include指令
  • xml 与dto的相互转换
  • ubuntu下安装cx_oracle
  • Android ViewPager使用详解
  • lateral view
  • Spring 一二事(10) - annotation AOP
  • 网络传输文件的问题
  • 【笔记】你不知道的JS读书笔记——Promise
  • Android 架构优化~MVP 架构改造
  • Brief introduction of how to 'Call, Apply and Bind'
  • git 常用命令
  • java2019面试题北京
  • Spring Boot MyBatis配置多种数据库
  • vuex 笔记整理
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • 阿里云购买磁盘后挂载
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 设计模式走一遍---观察者模式
  • 手写一个CommonJS打包工具(一)
  • 自制字幕遮挡器
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • ​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​
  • ​水经微图Web1.5.0版即将上线
  • #### go map 底层结构 ####
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • ${factoryList }后面有空格不影响
  • (1)bark-ml
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (二十三)Flask之高频面试点
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • (转)为C# Windows服务添加安装程序
  • (转载)深入super,看Python如何解决钻石继承难题
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .“空心村”成因分析及解决对策122344
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .net FrameWork简介,数组,枚举
  • .NET 使用配置文件
  • .net对接阿里云CSB服务
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • @Transactional 详解
  • [AI]文心一言爆火的同时,ChatGPT带来了这么多的开源项目你了解吗