session的token令牌机制防止表单重复提交

本例通过前端js+后端session的token机制完美解决表单重复提交问题:

表单重复提交产生的原因:

1.网络延迟,连续点击。(js控制连续点击导致重复提交问题)

2.提交成功后,刷新页面。(session的token控制)

3.提交成功后,后退,再次点击提交。(session的token控制)

提交页面:

<%@ 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>
    <title>Form表单</title>
    <script type="text/javascript">
        var isSubmitFlag = false;
        function isSubmit() {
            if(!isSubmitFlag){
                isSubmitFlag = true;
                return isSubmitFlag;
            }else{
                return false;
            }
        }
    </script>
</head>

<body>
<%--token能防止表单刷新,后退提交导致的重复提交,防止不了连续点击导致的重复提交。连续点击的估计得在前端js处理。--%>
<form action="${pageContext.request.contextPath}/submitForm" method="post" onsubmit="return isSubmit()">
    <input type="hidden" name="token" value="${token}">
    用户名:<input type="text" name="userName">
    <input type="submit" value="提交" id="submit">
</form>
</body>
</html>

token生成通过页面跳转控制器:

package com.cookie_session.formRepeat.token;

import com.cookie_session.customeSessionAndToken.TokenUtils;

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 java.io.IOException;

/**
 * Created by Administrator on 2021/6/28.
 */
@WebServlet("/toForm")
public class ToFormServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        session.setAttribute("token",TokenUtils.getSessionId());
        req.getRequestDispatcher("form_token.jsp").forward(req,resp);
    }
}

提交表单后处理控制器:

package com.cookie_session.formRepeat;

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 java.io.IOException;

/**
 * Created by Administrator on 2021/6/28.
 */
@WebServlet("/submitForm")
public class TokenFormServlet extends HttpServlet{
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String userName = (String) req.getParameter("userName");
        resp.setContentType("text/html;charset=utf-8");
        boolean isSubmitFlag = isSubmit(req);
        if(!isSubmitFlag){
            resp.getWriter().write("您已经提交过数据或者token错误!");
            return;
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("userName:" + userName);
        req.getSession().removeAttribute("token");
    }

    /*
        这个能防止表单刷新,后退返回导致的重复提交,防止不了连续点击导致的重复提交。
        连续点击的估计得在前端js处理。
     */
    private boolean isSubmit(HttpServletRequest req) {
        String paramToken = req.getParameter("token");
        String sessionToken = (String) req.getSession().getAttribute("token");
        if(sessionToken == null){
            return false;
        }
        if(!sessionToken.equals(paramToken)){
            return false;
        }
        return true;
    }
}

注意:

1.由于token机制在servlet或者controller中无法控制连续点击导致重复提交的问题。因为连续点击的时候,可能同时判断了isSubmit()方法,

这时候还没有提交完成,session中的token还没清空。所以需要前端js控制连续点击问题。

2.而提交成功后的刷新页面和后退再次提交,

都是servlet或者controller已经执行完成,此时session中的token已经清空。刷新或者后退的再次提交,sessionToken为空判断方法不为真。

直接返回"您已经提交过数据或者token错误!"

至此js+token完美解决表单重复提交问题。(此方法实用于单机系统,也就是单个服务器)。

集群,负载均衡轮询的话,多次请求可能访问到不同机器,不同机器jvm不同,session不同,这时候需要解决session共享问题。可使用redis解决。

token放在redis中,表单再次提交,从redis拿到token进行比较判断,确认是否重复提交。

原文地址:https://www.cnblogs.com/super-chao/p/14949504.html