lsekfe 发表于 2020-10-13 09:50:27

Web系统的表单重复提交问题及解决方案

表单页面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
以下为方案二测试结果:
场景一


场景二

场景三

场景四(只有后进入的表单页面有最新的Token)


页: [1]
查看完整版本: Web系统的表单重复提交问题及解决方案