javaWeb学习笔记-Session防止表单重复提交

一、什么情况下会重复提交表单

  • 在网络有延迟的情况下,用户多次点击提交按钮
  • 用户提交数据成功后,点击“刷新”按钮,导致表单重复提交
  • 用户提交表单后,“后退”到表单页面,再次提交,导致表单的重复提交

二、防止表单重复提交的方法

使用到的表单:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
  <head>
    <title>表单重复提交解决方法</title>
  </head>

  <body>
      <!-- 将表单的数据提交到FormServlet中 -->
      <form action="${pageContext.request.contextPath}/servlet/FormServlet" method="post">
        用户名:<input type="text" name="username">
        <input type="submit" value="提交" id="submit">
    </form>
  </body>
</html>

servlet代码:

package xdp.gacl.session;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FormServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //设置编码格式,防止产生乱码
        request.setCharacterEncoding("UTF-8");
        String userName = request.getParameter("username");
        try {
            //睡眠3秒,模拟网络延迟
            Thread.sleep(3*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 打印得到的数据
        System.out.println(userName);
    }

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

}

1、使用javaScript防止表单重复提交
具体的思路:在用户点击提交后,禁止再次点击“提交”按钮。
第一种实现方式(给表单添加事件处理):

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
  <head>
    <title>Form表单</title>
        <script type="text/javascript">
        //定义变量,表示调单是否可以提交
        var isCom = false;
        function dosubmit(){
            // 判断当前的isCom是否可以提交
            if(isCom==false){
                // 修改变量,表示表单已经提交
                isCom = true;
                return true;
            }else{
                return false;
            }
        }
    </script>
  </head>

  <body>
      <form action="${pageContext.request.contextPath}/servlet/FormServlet" onsubmit="return dosubmit()" method="post">
        用户名:<input type="text" name="username">
        <input type="submit" value="提交" id="submit">
    </form>
  </body>
</html>

第二种实现方式(提交后,将表单设置为不可用):

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
  <head>
    <title>Form表单</title>
        <script type="text/javascript">
            function dosubmit(){
               //获取表单提交按钮
               var btnSubmit = document.getElementById("submit");
               //将表单提交按钮设置为不可用,
               btnSubmit.disabled= "disabled";
            }
        }
    </script>
  </head>

  <body>
      <form action="${pageContext.request.contextPath}/servlet/FormServlet" method="post">
        用户名:<input type="text" name="username">
        <input type="submit" value="提交" id="submit" onsubmit="dosubmit()">
    </form>
  </body>
</html>

很明显,使用javaScript的方式来处理表单重复提交,是可以的。但是这种方式只能处理第一种场景的表单重复提交,对于第二、三种场景的表单重复提交是处理不了的。这时就可以使用session来解决后面两种场景的表单重复提交。

2、session处理表单重复提交
具体思路:在服务器端产生一个唯一标志符,将这个唯一标识符隐藏在表单中,表单提交时,判断两者是否相同,来进行相应的处理。这个唯一标识符又叫令牌。
什么情况下表示表单不是第一次提交

  • 表单中令牌个和服务器中令牌不一致
  • 表单中的令牌在服务器中不存在
  • 表单中没有令牌

    创建令牌的类:

// 单例模式
class Token{

    private static Token instance = new Token();

    private Token(){}

    public static Token getInstance(){
        return instance;
    }

    public static String getToken(){

        String token = System.currentTimeMillis()+new Random().nextInt()+"";

        // 使用MessageDigest 类 产生一个唯一随机数
        try {
            // 使用md5算法产生随机数
            MessageDigest md = MessageDigest.getInstance("md5");
            // 使用token数字,产生一个唯一的随机数
            byte[] md5 = md.digest(token.getBytes());

            // 在使用base64编码将随机数的位数固定
            BASE64Encoder encoder = new BASE64Encoder();
            return encoder.encode(md5);

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }

    }
}

得到一个令牌,存放到session域对象中,跳转到jsp页面:

package test.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;

/**
 * Servlet implementation class SessionDemo3
 * 
 *  SessionDemo3和4 一起实现 防止表单的重复提交
 *      这个负责产生随机数,发送给表单
 */
@WebServlet("/SessionDemo3")
public class SessionDemo3 extends HttpServlet {
    private static final long serialVersionUID = 1L;

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

        // 产生随机数
        String token = Token.getInstance().getToken();

        // 将令牌存到session中
        HttpSession session = request.getSession();
        session.setAttribute("token", token);

        // 转发到 显示 表单页面
    request.getRequestDispatcher("/Session34.jsp").forward(request, response);

    }

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

}

表单界面:

<%@ 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=GB18030">
<title>Insert title here</title>
</head>
<body>

    <!-- 将表单提交给SessionDemo4进行处理 -->
    <form action="/javaWeb/SessionDemo4" method="post">
        <!-- 隐藏的令牌 -->
        <input type="hidden" name="token" value="${token }">
        名字:<input type="text" name="name"> <br>
        <input type="submit" value="注册">
    </form>

</body>
</html>

表单的处理页面:

package test.session;

import java.io.IOException;
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 SessionDemo4
 * 
 *  这个页面负责 处理表单发的请求
 * 
 */
@WebServlet("/SessionDemo4")
public class SessionDemo4 extends HttpServlet {
    private static final long serialVersionUID = 1L;

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

        boolean b = isToken(request);
        if(!b){
            response.getOutputStream().write("请不要重复提交".getBytes());
            System.out.println("请不要重复提交");
            return ;
        }

        // 将token从服务器端删除
        request.getSession().removeAttribute("token");
        System.out.println("注册成功");
        response.getOutputStream().write("注册成功".getBytes());
    }

    // 将表单中 token 和 服务器的session进行对比
    private boolean isToken(HttpServletRequest request) {

        // 获取表单的token
        String token = request.getParameter("token");
        if(token==null){
            return false;
        }

        // 获取已经创建的session
        HttpSession s = request.getSession(false);
        if(s==null){
            return false;
        }

        // 获取session域对象中的令牌
        String ss = (String) s.getAttribute("token");
        if(ss==null){
            return false;
        }

        // 判断表单的token是否和session的tokrn相同
        if(!token.equals(ss)){
            return false;
        }

        return true;
    }

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

}

这样就能处理场景二、三的表单重复提交。在实际开发中一般都是要结合javaScript和session一起来防止所有的场景的表单提交。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值