Ways to avoid duplicate submissions of Java forms

  • 2020-05-26 08:20:35
  • OfStack

Repeated submission of the form: request the form page before completing it once - > Submit the form process to complete the data submission

Root cause: did not complete 1, first request form page - > Resubmit the form process.

Duplicate submissions:

Click the submit button repeatedly due to slow server or network delay. Successfully submitted, refresh the success page (forward)(request forward). Submitted successfully, click the submit button again by backing out

Note: when you step back, refresh the form page, and submit again, instead of repeating the submission, you send a new request. Under Firefox, a repeat submission to the same address is invalid.

Case study:


@WebServlet("/trans")
public class TransferServlet extends HttpServlet{
  private static final long serialVersionUID = 1L;
  
  protected void service(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    req.setCharacterEncoding("UTF-8");
    resp.setContentType("text/html;charset=UTF-8");
    PrintWriter out = resp.getWriter();
    String money = req.getParameter("money");
    // By sleep , Analog network delay 
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println(" transfer-out amount :"+money);
    out.print(" transfer-out amount :"+money);  
  }
}

Below the crazy point, you will find that the jsp page will not change, but you can see from the background printout that it will continue to output, indicating that 1 is being executed

Solutions:

Before you can guarantee to submit the guarantee, you must first request the form interface, the principle and the captcha 1

In the first time I request, the request to the forms interface, create a token, when click on transfer, send the request, take this token, sent to the next one interface, in servlet to validate this token, the token in session 1, 1 in the form, equal to form correct, and the token of the session were destroyed.

The code on the jsp page


<%
      // Create a token 
      String token = java.util.UUID.randomUUID().toString();
      // There are session In the 1 Copy of the , Judge later 
      session.setAttribute("TOKEN_IN_SESSION", token);
    %>
    
    <h3> Transfer interface </h3>
    <form action="/trans" method="post">
      <input type="hidden" name="token" value="<%=token%>"/>
       Transfer amount :<input type="text" name="money" min="1" required /><br/>
      <input type="submit" value=" transfer " />
    </form>

Code in TransferServlet


@WebServlet("/trans")
public class TransferServlet extends HttpServlet{
  private static final long serialVersionUID = 1L;
  
  protected void service(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    req.setCharacterEncoding("UTF-8");
    resp.setContentType("text/html;charset=UTF-8");
    PrintWriter out = resp.getWriter();
    // Get in the form token value 
    String token = req.getParameter("token");
    // To obtain session In the token value 
    String sessionToken = (String) req.getSession().getAttribute("TOKEN_IN_SESSION");
    //session In the token Easy is empty , because session In the token It needs to be destroyed 
    
    if (token.equals(sessionToken)) {
      // Indicates that the token is the same 
      req.getSession().removeAttribute("TOKEN_IN_SESSION");
      String money = req.getParameter("money");
      System.out.println(" transfer-out amount :"+money);
      out.print(" transfer-out amount :"+money);
      // Final destruction session The token 
    }
    // If the token is different, it is repeatedly submitted , Can't submit 
  }
}

Then, to avoid the Java code in the jsp file, create and jump the token into another servlet


@WebServlet("/transfer")
public class CopyOfTransferServlet extends HttpServlet{
  private static final long serialVersionUID = 1L;
  
  protected void service(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    // Create a token , And to jump to submit.jsp
    String token = UUID.randomUUID().toString();
    System.out.println(token);
    req.getSession().setAttribute("TOKEN_IN_SESSION", token);
    req.setAttribute("token", token);
    req.getRequestDispatcher("/views/repeatsubmit/submit.jsp").forward(req, resp);
    
  }
}

This is what the jsp file looks like at this point


<h3> Transfer interface </h3>
    <form action="/trans" method="post">
      <input type="hidden" name="token" value="${token }"/>
       Transfer amount :<input type="text" name="money" min="1" required /><br/>
      <input type="submit" value=" transfer " />
    </form>

It's a lot cleaner and a lot neater than the one above, but it doesn't feel good, because if you want to use it somewhere else, you need to create tokens, validate tokens, delete tokens, so pull it out and make a tool class

TokenUtil.java


// The token's utility class 
// Create a token 
// Check the token 
// Destruction of the token 
public class TokenUtil {
  private final static String TOKEN_IN_SESSION = "TOKEN_IN_SESSION";
  public static void savaToken(HttpServletRequest req) {
    String token = UUID.randomUUID().toString();
    System.out.println(token);
    req.getSession().setAttribute(TOKEN_IN_SESSION, token);
    req.setAttribute("token", token);
  }

  public static boolean validateToken(HttpServletRequest req,
      String tokenInrequest) {
    // To obtain session In the token value 
    String sessionToken = (String) req.getSession().getAttribute(
        TOKEN_IN_SESSION);
    if (tokenInrequest.equals(sessionToken)) {
      req.getSession().removeAttribute(TOKEN_IN_SESSION);
      return true;
    }
    return false;
  }
}

This is good, the consumer just needs to call this utility class, no need to write create token and so on 1 series operation.


Related articles: