防止表单重复提交

一、场景
1、由于用户误操作,多次点击表单提交按钮。
2、由于网速等原因造成页面卡顿,用户重复刷新提交页面。
3、黑客或恶意用户使用postman等工具重复恶意提交表单(攻击网站)。
 
二、解决方案
1、通过JavaScript屏蔽提交按钮(不推荐)
通过js代码,当用户点击提交按钮后,屏蔽提交按钮使用户无法点击提交按钮或点击无效,从而实现防止表单重复提交。
缺点:js代码很容易被绕过。比如用户通过刷新页面方式,或使用postman等工具绕过前段页面仍能重复提交表单。因此不推荐此方法。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
        <!DOCTYPE HTML>
        <html>
        <head>
         <title>表单</title>
            <script type="text/javascript">
            //默认提交状态为false
            var commitStatus = false;
            function dosubmit(){
                  if(commitStatus==false){
                //提交表单后,讲提交状态改为true
                  commitStatus = true;
                  return true;
                 }else{
                  return false;
              }
             }
      </script>
     </head>
   
        <body>
            <form action="/path/post" onsubmit="return dosubmit()" method="post">
             用户名:<input type="text" name="username">
            <input type="submit" value="提交" id="submit">
            </form>
        </body>
    </html>
View Code
2、给数据库增加唯一键约束(简单粗暴)
在数据库建表的时候在ID字段添加主键约束,用户名、邮箱、电话等字段加唯一性约束。确保数据库只可以添加一条数据。
-- 数据库加唯一性约束sql:
alter table tableName_xxx add unique key uniq_xxx(field1, field2)
缺点:通过数据库加唯一键约束能有效避免数据库重复插入相同数据。但无法阻止恶意用户重复提交表单(攻击网站),服务器大量执行sql插入语句,增加服务器和数据库负荷。
 
3、利用Session防止表单重复提交(推荐)
1】实现原理:
服务器返回表单页面时,会先生成一个token保存于session,并把该token传给表单页面。当表单提交时会带上token,服务器拦截器Interceptor会拦截该请求,拦截器判断session保存的token和表单提交token是否一致。若不一致或session的token为空或表单未携带token则不通过。
 
首次提交表单时session的token与表单携带的token一致走正常流程,然后拦截器内会删除session保存的token。当再次提交表单时由于session的token为空则不通过。从而实现了防止表单重复提交。
2】Struts2实现
// 1、要防止重复提交的jsp页面:
<s:token /><!-- 生成令牌号,防止表单重复提交 -->
// 2、struts.xml
<package name="default" namespace="/" extends="struts-default">
        <action name="regist" class="cn.itcast.action.RegistAction">
            <result name="invalid.token">/token.jsp</result><!-- name必须是: invalid.token -->
            <interceptor-ref name="token" />
            <interceptor-ref name="defaultStack" />
        </action>
    </package>
// 3、token.jsp
 您已经重复提交表单,请刷新后重试! <br>

3】session实现

// jsp页面
<% //生成一个令牌
      String token=UUID.randomUUID().toString();
      session.setAttribute("token", token);
%>
<form action="${pageContext.request.contextPath}/regist" method="get">
          <input type="hidden" name="token" value="<%=token%>">
          username:<input type="text" name="username"><br>
          password:<input type="password" name="password"><br>
          <input type="submit" value="注册">
      </form>
    
// RegistServlet.java
public class RegistServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        String token = request.getParameter("token");
        String _token = (String) request.getSession().getAttribute("token");
        request.getSession().removeAttribute("token");
        if (token.equals(_token)) {
            System.out.println("将" + username + " 与" + password
                    + "存储到数据库中,完成注册");
        } else {
            response.getWriter().write("不允许表单重复提交");
        }
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}
4、使用AOP自定义切入实现
原文地址:https://www.cnblogs.com/shelly0307/p/10916024.html