表单页面JSP:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>表单页面</title> </head> <body> <form action="commit" method="post"> <input type="text" name="username" /> <input type="submit" id="submit"/> </form> </body> </html> |
表单处理Servlet
public class FormServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { Thread.sleep(2000); // 模拟网络延迟 } catch (InterruptedException e) { e.printStackTrace(); } req.setCharacterEncoding("utf-8"); System.out.println("对" + req.getParameter("username") + "进行处理"); } } |
用户重复提交的场景
只列举了常见的场景
场景一:表单提交后,因为网络延迟,让用户有时间重复点击提交。
场景二:表单提交后,用户刷新页面,导致表单重复提交。
场景三:表单提交后,用户退回上一个页面,再次点击提交。
场景四:在一个浏览器中,用户打开两个标签页进行提交。
重复提交的解决方案
2.1 方案一
在前端,通过设置一个标识变量,标识表单的提交状态。(只能解决场景一)
标识变量默认为false,一旦表单提交触发,会判断标识变量是否为false,如果为false则发送请求并且将标识变量更改为true,如果为true则不发送请求。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>表单页面</title> <script type="text/javascript"> var isCommitted = false; // 默认未提交 function doSubmit() { if (isCommitted == false) { isCommitted = true; // 改为已提交 return true; }else { return false; } } </script> </head> <body> <form action="commit" method="post" onsubmit="return doSubmit()"> <!--通过doSubmit函数,动态地给onsubmit属性赋值--> <input type="text" name="username" /> <input type="submit" id="submit"/> </form> </body> </html> |
2.2 方案二
在后端,通过session和唯一Token来判断重复提交。(可以解决四个场景)
服务器通过session为用户保存一个唯一Token,并且给到浏览器,浏览器会将其保存在一个隐藏的域中随表单提交。
当第一次提交时,提交的Token与session中的Token相等,进行相应处理,并且删除session中的Token。
1.TokenProcessor的工具类
public class TokenProcessor { private static final TokenProcessor instance = new TokenProcessor(); public static TokenProcessor getInstance() { return instance; } public String makeToken() { String token = String.valueOf(System.currentTimeMillis() + new Random().nextInt(999999999)); try { MessageDigest md = MessageDigest.getInstance("md5"); byte md5[] = md.digest(token.getBytes()); Base64.Encoder encoder = Base64.getEncoder(); return encoder.encodeToString(md5); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } } } |
2.获取session和Token的Servlet
public class GetSessionServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String token = TokenProcessor.getInstance().makeToken(); System.out.println(token); // 打印Token的值 req.getSession(true).setAttribute("token", token); req.getRequestDispatcher("/index.jsp").forward(req, resp); } } |
3.表单页面JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>表单页面</title> </head> <body> <form action="commit" method="post"> <input type="text" name="username" /> <input type="hidden" name="token" value=${sessionScope.token} /> <input type="submit" id="submit"/> </form> </body> </html> |
4.表单处理Servlet
public class FormServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { Thread.sleep(2000); // 模拟网络延迟 } catch (InterruptedException e) { e.printStackTrace(); } req.setCharacterEncoding("utf-8"); HttpSession session = req.getSession(false); if (session == null) { System.out.println("未拥有session,不处理"); return; } String token = (String)session.getAttribute("token"); if (token == null) { System.out.println("session中未拥有Token,不处理"); return; } if (req.getParameter("token") == null) { System.out.println("浏览器Token为空,不处理"); } if (!token.equals(req.getParameter("token"))) { System.out.println("Token不一致,不处理"); return; } session.removeAttribute("token"); System.out.println("对" + req.getParameter("username") + "进行处理"); } } |
以下为方案二测试结果:
场景一
场景二
场景三
场景四(只有后进入的表单页面有最新的Token)
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理