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进行比较判断,确认是否重复提交。