Java防止非法和重復表單提交的方法


能解決一切的,目前應該還是離不開session。

目標:

       1、當用戶進行的是Refresh/Reload/Back/Forward操作、以及先BackSubmit操作時,僅僅是reloading先前的結果頁。

       2、當用戶重復提交同一個任務操作時,后台服務接收並處理第一次提交的任務,后面提交不起作用(不轉向也不提示)。

       3、該功能具有公用性。

基本思路:

       1、在basic filter中實現公用性

                if(true){//問題1:如何確定是否為重復提交

                         ...

                         chain.doFilter(request,response);

                }else{

                         //問題2:如何實現不轉向、不提示也不顯示空白頁

                }

       2、網上資料概括

                a、提交表單后按鈕變灰/隱藏提交按鈕

                b、在js里設置全局變量,提交后修改該變量的值,依據變量的值判斷是否重復提交

                         var flag=true;

                         function checkForm(){

                                   if (flag==false){

                                            return;

                                   }

                        

                                   flag=false;

                                   document.form1.submit();

                        

                         }

                cstruts webwork沒有找到這個資料)

                         //驗證事務控制令牌,<html:form >會自動根據session中標識生成一個隱含input代表令牌,防止兩次提交

                         action中:

                        

                                //<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae">

                        

                                if (!isTokenValid(request))

                                    errors.add(ActionErrors.GLOBAL_ERROR,

                                               new ActionError("error.transaction.token"));

                                resetToken(request); //刪除session中的令牌

                        

                         action有這樣的一個方法生成令牌華

                            protected String generateToken(HttpServletRequest request) {

                        

                                HttpSession session = request.getSession();

                                try {

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

                                    byte now[] =

                                        new Long(System.currentTimeMillis()).toString().getBytes();

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

                                    md.update(id);

                                    md.update(now);

                                    return (toHex(md.digest()));

                                } catch (IllegalStateException e) {

                                    return (null);

                                } catch (NoSuchAlgorithmException e) {

                                    return (null);

                                }

                        

                            }          

                d、用戶使用瀏覽器時,可以經常使用向后的按鈕,因此就有可能重復提交一個他們已經提交過的form,這樣就會帶來一個重復事務處理的問題。同樣,一個用戶也可能在接收到一個確認的頁面之前按下停止的按鈕,接着再次提交同一個form。對於這些情況,我們都想跟蹤並且禁止這些重復的提交,我們可以使用一個控制servlet來提供一個控制點,以解決這個問題。

                  同步記號(Synchronizer (or Dvu) Token

               

這個策略是為了解決重復的form提交問題。一個同步的記號被設置在一個用戶的Session中,並且包含在返回到客戶的每一個form中。當form被提交時,form中的同步標記就和Session中的同步標記作對比。在form首次提交的時候,這兩個標記應該是一樣的。如果標記不一樣,那么該form就會禁止提交,一個錯誤就會返回給用戶。在用戶提交一個form時,如果按下瀏覽器中的后退按鈕並嘗試重新提交同一個form時,標記就會出現不匹配的現象。

另一方面,如果兩個標記值匹配,那么我們就可以確信整個流程是正確的。在這種情況下,Session中的標記值就會被修改為一個新的值,同時允許提交該form。                                   你也可以使用這個策略來控制對某些頁面的直接訪問,就好象上面資源保護中描述的一樣。例如,假設一個用戶將某個應用的頁面A收藏到收藏夾中,而頁面A只允許通過頁面B和C訪問。當用戶直接通過收藏夾來訪問頁面A,這時頁面的訪問順序就是不正確的,這樣同步標記將處在一個不同步的狀態,或者它根本就不存在。不論怎樣,訪問都被禁止了。                 e、做一個hidden框,名字自己定,提交后得到這個值放入session,提交前判斷session是否為空   解決方案:        1、后台公共類中實現前台的Form中自動生成兩個hidden文本功能,一個是作page是否重復提交判斷,並由系統自動附上關鍵值(如struts采用的方案);另一個作為button是否重復提交判斷(struts中好像沒有)。由后台公共類實現界面兩個hidden text自動生成的好處在於公用性。        2、在basic filter中根據兩個hidden text值判斷是否為重復提交。        3、javascript中作一個公共方法,實現功能:如果需要判斷是否重復提交,就給第二個hidden text附上關鍵值,並使該功能不可用。 . 個人感想:我相信未來該功能一定會被服務器集成,而不再由開發人員進行編碼.

----------------------------------------示例代碼----------------------------------------------------

第一,對於不支持POST的,可以簡單的使用如下代碼
if ("POST".equals(request.getMethod())) ...{
  // 正常進行
}else...{
  // 異常請求
  out.print("異常訪問");
  return;
}
如果是servlet, 可以將doGet方法直接返回,不進行處理就行了
public void doGet(HttpServletRequest request, HttpServletResponse response) ...{
  return;
}
public void doPost(HttpServletRequest request, HttpServletResponse response) ...{
  // 正常進行操作
}
還可以采用特定的標志來區分,比如 
<form><input type="hidden" name="action" value="insert"/></form> 
程序里這樣判斷
if ("POST".equals(request.getMethod()) && ("insert".equals(request.getParameter("action")))) ...{
  // 正常進行
}else...{
  // 異常請求
  out.print("異常訪問");
  return;
}
第二,判斷提交的來源referer,代碼如下
if ("POST".equals(request.getMethod())) ...{
  String referer = request.getHeader("referer");
  if (referer == null || !referer.startsWith("http://"+request.getServerName())) ...{
    // 非法來源
    return;
  }
  // 正常進行
}else...{
  // 異常請求
  out.print("異常訪問");
  return;
}
第三 防止重復提交的hashCode 
在表單顯示頁面
  //生成一個formhash,算法可以自己定,不隨便重復就可以了
  String formhash = MD5.encode(Long.toString(new Date().getTime()));
  //讀取當前session里面的hashCode集合,此處使用了Set,方便判斷。
  Set<String> formhashSession = (Set<String>) session.getAttribute("formhashSession");
  if (formhashSession == null) ...{
    formhashSession = new HashSet<String>();
  }
  // 檢測重復問題
  while (formhashSession.contains(formhash)) ...{
    formhash = MD5.encode(Long.toString(new Date().getTime()));
  }
  // 保存到session里面
  formhashSession.add(formhash);
  // 保存
  session.setAttribute("formhashSession", formhashSession);
表單里面增加如下字段
<input type="hidden" name="formhash" id="formhash" value="<%=formhash%>" /> 
在表單提交頁面進行如下處理
    // 拿到表單的formhash
    String formhash = upload.getParameter("formhash");
    // 拿到session里面的集合
    Set<String> formhashSession = (Set<String>) session.getAttribute("formhashSession");
    // 如果沒有,則是重復提交,或者非法提交
    if (formhashSession == null || !formhashSession.contains(formhash)) ...{
      out.println("請不要重復提交!");
      return;
    }
    // 下面進行其它的操作
    // 
    // 最后,如果操作成功,從session里面把這個formhash 刪掉!
    // 以免用戶少填寫了某個字段,造成表單無法再次提交
    formhashSession.remove(formhash);
    session.setAttribute("formhashSession", formhashSession);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM