天天看點

java web項目防止表單重複送出的實作方案

當使用者在表單中填寫完資訊,單擊“送出”按鈕後,可能會因為沒有看到成功資訊而再次單擊“送出”按鈕,進而導緻在服務端接收到兩條同樣的資訊,如果這個資訊是要儲存到資料庫裡的,那麼就會出現兩條相同的資訊,而這往往往會引起資料庫異常,對整個系統的穩定運作會産生緻命的危害。在實際應用中,由于使用者沒有及時看到響應資訊而導緻的重複送出時有發生。響應不及時有可能是因為這個時段伺服器的負載較大,又或者這個處理本身就是比較耗時的操作。

     有時候,即使響應及時,也有可能會出現重複送出的情況。伺服器端的程式在處理完使用者送出的資訊後,調用了RequestDispatcher.forward()方法将使用者的請求轉發給成功頁面,使用者看到成功資訊後,單了浏覽器的“重新整理”按鈕,此時浏覽器會再次送出使用者先前輸入的資料,這是因為調用了RequestDispatcher.forward()方法,浏覽器所保留的URL是先前表單送出的URL,如果是采用了RequestDispatcher.sendRedircert()方法将用戶端重定向到成功頁面,就不會出現重複送出的問題了。

下面用用戶端與伺服器端令牌相結合的方式,防止使用者重複送出表單。

廢話少說,出代碼

本示例項目檔案結構如下圖:

java web項目防止表單重複送出的實作方案

login.jsp頁面代碼如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<%@ page import="com.test.TokenProcessor" %>

<%@ page contentType="text/html; charset=UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

    <title>防止表單重複送出</title>

    <script type="text/javascript">

     <!--

      var checkSubmitFlg=true;

      function checkSubmit(){

       if(true==checkSubmitFlg){

        document.theForm.btnSubmit.disable=true;

        document.theForm.submit();

        checkSubmitFlg=false;

       }else{

        alert("你已經送出 了表單,請不要重複送出!");

       }

      }

     //-->

    </script>

  </head>

  <body>

   <%

    TokenProcessor processor=TokenProcessor.getInstance();

    String token=processor.getToken(request);

    %>

 <form action="handler" name="theForm" method="post">

  <table>

   <tr>

    <td>使用者名:</td>

    <td><input type="text" name="username"/></td>

   </tr>

   <tr>

    <td>郵件位址:</td>

    <td>

     <input type="text" name="email"/>

     <input type="hidden" name="ltai701" value="<%=token %>"/>

    </td>

   </tr>

   <tr>

    <td><input type="reset" value="重填"/></td>

    <td><input type="button" value="送出" name="btnSubmit" onclick="checkSubmit()"/></td>

   </tr>

  </table>

 </form>

  </body>

</html>

HandlerServlet代碼如下:

package com.test;

import java.io.IOException;

import java.io.PrintWriter;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

public class HandlerServlet extends HttpServlet {

 private static final long serialVersionUID = 1L;

 int count=0;

 protected void doGet(HttpServletRequest req, HttpServletResponse resp)

   throws ServletException, IOException {

  doPost(req, resp);

 }

 protected void doPost(HttpServletRequest req, HttpServletResponse resp)

   throws ServletException, IOException {

  resp.setContentType("text/html;charset=UTF-8");

  PrintWriter out=resp.getWriter();

  TokenProcessor processor=TokenProcessor.getInstance();

  if(processor.isTokenValid(req)){

  System.out.println("submit:"+count);

  if(count%2==1) count=0;

  else count++;

  out.println("success");

  }else{

   processor.saveToken(req);

   out.println("你已經送出了表單,同一表單不能兩次送出");

  }

  out.close();

 }

}

TokenProcessor代碼如下:

package com.test;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

public class TokenProcessor {

 static final String TOKEN_KEY="ltai701";

 private static TokenProcessor instance=new TokenProcessor();

 public static TokenProcessor getInstance(){

  return instance;

 }

 private long previous;

 public synchronized boolean isTokenValid(HttpServletRequest request){

  //得到請求的目前session對象

  HttpSession session=request.getSession(false);

  if(session==null){

   return false;

  }

  //從session中取出儲存的令牌值

  String saved=(String)session.getAttribute(TOKEN_KEY);

  if(saved==null){

   return false;

  }

  //清除session中的令牌值

  resetToken(request);

  //得到請求參數中的令牌值

  String token=request.getParameter(TOKEN_KEY);

  if(token==null){

   return false;

  }

  return saved.equals(token);

 }

 public synchronized void resetToken(HttpServletRequest request){

  HttpSession session=request.getSession(false);

  if(session==null){

   return;

  }

  session.removeAttribute(TOKEN_KEY);

 }

 public synchronized void saveToken(HttpServletRequest request){

  HttpSession session=request.getSession(false);

  String token=generateToken(request);

  if(token!=null){

   session.setAttribute(TOKEN_KEY, token);

  }

 }

 public synchronized String  generateToken(HttpServletRequest request){

  HttpSession session =request.getSession(false);

  try{

   byte id[]=session.getId().getBytes();

   long current=System.currentTimeMillis();

   if(current==previous){

    current++;

   }

   previous=current;

   byte now[]=new Long(current).toString().getBytes();

   MessageDigest md=MessageDigest.getInstance("MD5");

   md.update(id);

   md.update(now);

   return toHex(md.digest());

  }catch (NoSuchAlgorithmException e) {

   // TODO: handle exception

   e.printStackTrace();

   return null;

  }

 }

 private String toHex(byte buffer[]){

  StringBuffer sb=new StringBuffer(buffer.length*2);

  for(int i=0;i<buffer.length;i++){

   sb.append(Character.forDigit((buffer[i]&0xf0)>>4, 16));

   sb.append(Character.forDigit(buffer[i]&0x0f, 16));

  }

  return sb.toString();

 }

 public synchronized String getToken(HttpServletRequest request){

  HttpSession session=request.getSession(false);

  if(null==session)

   return null;

  String token=(String)session.getAttribute(TOKEN_KEY);

  if(null==token){

   token=generateToken(request);

   if(token!=null){

    session.setAttribute(TOKEN_KEY,token);

    return token;

   }else

    return null;

  }else

   return token;

 }

}

web.xml配置檔案如下:

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4"

 xmlns="http://java.sun.com/xml/ns/j2ee"

 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

 <servlet>

  <servlet-name>HanderServlet</servlet-name>

  <servlet-class>com.test.HandlerServlet</servlet-class>

 </servlet>

 <servlet-mapping>

  <servlet-name>HanderServlet</servlet-name>

  <url-pattern>/handler</url-pattern>

 </servlet-mapping>

  <welcome-file-list>

    <welcome-file>index.jsp</welcome-file>

    <welcome-file>login.jsp</welcome-file>

  </welcome-file-list>

</web-app>

運作效果圖如下:

等待伺服器端響應時重複按“送出”按鈕

java web項目防止表單重複送出的實作方案

送出完成後再重新整理浏覽器或者按回退鍵再按前進鍵,則有

java web項目防止表單重複送出的實作方案

希望此文章能幫助到有需要的人,多謝。