1. 程式人生 > >如何防止表單重複提交(token令牌)

如何防止表單重複提交(token令牌)

在伺服器端生成一個唯一的隨機標識號,專業術語稱為Token(令牌),同時在當前使用者的Session域中儲存這個Token。然後將Token傳送到客戶端的Form表單中,在Form表單中使用隱藏域來儲存這個Token,表單提交的時候連同這個Token一起提交到伺服器端,然後在伺服器端判斷客戶端提交上來的Token與伺服器端生成的Token是否一致,如果不一致,那就是重複提交了,此時伺服器端就可以不處理重複提交的表單。如果相同則處理表單提交,處理完後清除當前使用者的Session域中儲存的標識號

在下列情況下,伺服器程式將拒絕處理使用者提交的表單請求:


1.儲存Session域中的Token(令牌)與表單提交的Token(令牌)不同。

2.當前使用者的Session中不存在Token(令牌)。

3. 使用者提交的表單資料中沒有Token(令牌)。

  1. package item.google.session;  
  2. import java.io.IOException;  
  3. import javax.servlet.ServletException;  
  4. import javax.servlet.http.HttpServlet;  
  5. import javax.servlet.http.HttpServletRequest;  
  6. import javax.servlet.http.HttpServletResponse;  
  7. publicclass FormServlet extends HttpServlet {  
  8.     privatestaticfinallong serialVersionUID = -884689940866074733L;  
  9.     publicvoid doGet(HttpServletRequest request, HttpServletResponse response)  
  10.             throws ServletException, IOException {  
  11.         String token = TokenProccessor.getInstance().makeToken();//建立令牌
  12.         System.out.println("在FormServlet中生成的token:"+token);  
  13.         request.getSession().setAttribute("token", token);  //在伺服器使用session儲存token(令牌)
  14.         request.getRequestDispatcher("/form.jsp").forward(request, response);//跳轉到form.jsp頁面
  15.     }  
  16.     publicvoid doPost(HttpServletRequest request, HttpServletResponse response)  
  17.             throws ServletException, IOException {  
  18.         doGet(request, response);  
  19.     }  
  20. }  

2.生成Token的工具類TokenProccessor
  1. package item.google.session;  
  2. import java.security.MessageDigest;  
  3. import java.security.NoSuchAlgorithmException;  
  4. import java.util.Random;  
  5. import sun.misc.BASE64Encoder;  
  6. publicclass TokenProccessor {  
  7.     /* 
  8.      *單例設計模式(保證類的物件在記憶體中只有一個) 
  9.      *1、把類的建構函式私有 
  10.      *2、自己建立一個類的物件 
  11.      *3、對外提供一個公共的方法,返回類的物件 
  12.      */
  13.     private TokenProccessor(){}  
  14.     privatestaticfinal TokenProccessor instance = new TokenProccessor();  
  15.     /** 
  16.      * 返回類的物件 
  17.      * @return 
  18.      */
  19.     publicstatic TokenProccessor getInstance(){  
  20.         return instance;  
  21.     }  
  22.     /** 
  23.      * 生成Token 
  24.      * Token:Nv6RRuGEVvmGjB+jimI/gw== 
  25.      * @return 
  26.      */
  27.     public String makeToken(){  //checkException
  28.         //  7346734837483  834u938493493849384  43434384
  29.         String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";  
  30.         //資料指紋   128位長   16個位元組  md5
  31.         try {  
  32.             MessageDigest md = MessageDigest.getInstance("md5");  
  33.             byte md5[] =  md.digest(token.getBytes());  
  34.             //base64編碼--任意二進位制編碼明文字元   adfsdfsdfsf
  35.             BASE64Encoder encoder = new BASE64Encoder();  
  36.             return encoder.encode(md5);  
  37.         } catch (NoSuchAlgorithmException e) {  
  38.             thrownew RuntimeException(e);  
  39.         }  
  40.     }  
  41. }  

3.DoFormServlet處理表單提交
  1. package item.google.session;  
  2. import java.io.IOException;  
  3. import javax.servlet.ServletException;  
  4. import javax.servlet.http.HttpServlet;  
  5. import javax.servlet.http.HttpServletRequest;  
  6. import javax.servlet.http.HttpServletResponse;  
  7. publicclass DoFormServlet extends HttpServlet {  
  8.     publicvoid doGet(HttpServletRequest request, HttpServletResponse response)  
  9.                 throws ServletException, IOException {  
  10.             boolean b = isRepeatSubmit(request);//判斷使用者是否是重複提交
  11.             if(b==true){  
  12.                 System.out.println("請不要重複提交");  
  13.                 return;  
  14.             }  
  15.             request.getSession().removeAttribute("token");//移除session中的token
  16.             System.out.println("處理使用者提交請求!!");  
  17.         }  
  18.         /** 
  19.          * 判斷客戶端提交上來的令牌和伺服器端生成的令牌是否一致 
  20.          * @param request 
  21.          * @return  
  22.          *         true 使用者重複提交了表單  
  23.          *         false 使用者沒有重複提交表單 
  24.          */
  25.         privateboolean isRepeatSubmit(HttpServletRequest request) {  
  26.             String client_token = request.getParameter("token");  
  27.             //1、如果使用者提交的表單資料中沒有token,則使用者是重複提交了表單
  28.             if(client_token==null){  
  29.                 returntrue;  
  30.             }  
  31.             //取出儲存在Session中的token
  32.             String server_token = (String) request.getSession().getAttribute("token");  
  33.             //2、如果當前使用者的Session中不存在Token(令牌),則使用者是重複提交了表單
  34.             if(server_token==null){  
  35.                 returntrue;  
  36.             }  
  37.             //3、儲存在Session中的Token(令牌)與表單提交的Token(令牌)不同,則使用者是重複提交了表單
  38.             if(!client_token.equals(server_token)){  
  39.                 returntrue;  
  40.             }  
  41.             returnfalse;  
  42.         }  
  43.     publicvoid doPost(HttpServletRequest request, HttpServletResponse response)  
  44.             throws ServletException, IOException {  
  45.         doGet(request, response);  
  46.     }  
  47. }  

4.在form.jsp中使用隱藏域來儲存Token(令牌)
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  3. <html>  
  4. <head>  
  5. <title>form表單</title>  
  6. </head>  
  7. <body>  
  8.     <form action="${pageContext.request.contextPath}/servlet/DoFormServlet" method="post">  
  9.         <%--使用隱藏域儲存生成的token--%>  
  10.         <%--  
  11.             <input type="hidden" name="token" value="<%=session.getAttribute("token") %>">  
  12.         --%>  
  13.         <%--使用EL表示式取出儲存在session中的token--%>  
  14.         <input type="hidden" name="token" value="${token}"/>   
  15.         使用者名稱:<input type="text" name="username">   
  16.         <input type="submit" value="提交">  
  17.     </form>  
  18. </body>  
  19. </html>