熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java開源技術 >> 正文

用Struts的Token機制解決表單重復提交

2013-11-23 20:12:20  來源: Java開源技術 

  Struts的Token(令牌)機制能夠很好的解決表單重復提交的問題基本原理是服務器端在處理到達的請求之前會將請求中包含的令牌值與保存在當前用戶會話中的令牌值進行比較看是否匹配在處理完該請求後且在答復發送給客戶端之前將會產生一個新的令牌該令牌除傳給客戶端以外也會將用戶會話中保存的舊的令牌進行替換這樣如果用戶回退到剛才的提交頁面並再次提交的話客戶端傳過來的令牌就和服務器端的令牌不一致從而有效地防止了重復提交的發生
  
  這時其實也就是兩點第一你需要在請求中有這個令牌值請求中的令牌值如何保存其實就和我們平時在頁面中保存一些信息是一樣的通過隱藏字段來保存保存的形式如 〈input type=hidden name=orgapaclTOKEN value=aaffdcccae這個value是TokenProcessor類中的generateToken()獲得的是根據當前用戶的session id和當前時間的long值來計算的第二在客戶端提交後我們要根據判斷在請求中包含的值是否和服務器的令牌一致因為服務器每次提交都會生成新的Token所以如果是重復提交客戶端的Token值和服務器端的Token值就會不一致下面就以在數據庫中插入一條數據來說明如何防止重復提交
  
  在Action中的add方法中我們需要將Token值明確的要求保存在頁面中只需增加一條語句saveToken(request);如下所示
  
  public ActionForward add(ActionMapping mapping ActionForm form
  
  HttpServletRequest request HttpServletResponse response)
  
  //前面的處理省略
  
  saveToken(request);
  
  return mappingfindForward(add);
  
  }在Action的insert方法中我們根據表單中的Token值與服務器端的Token值比較如下所示
  
  public ActionForward insert(ActionMapping mapping ActionForm form
  
  HttpServletRequest request HttpServletResponse response)
  
  if (isTokenValid(request true)) {
  
  // 表單不是重復提交
  
  //這裡是保存數據的代碼
  
  } else {
  
  //表單重復提交
  
  saveToken(request);
  
  //其它的處理代碼
  
  }
  
  }
  
  其實使用起來很簡單舉個最簡單最需要使用這個的例子
  
  一般控制重復提交主要是用在對數據庫操作的控制上比如插入更新刪除等由於更新刪除一般都是通過id來操作(例如updateXXXById removeXXXById)所以這類操作控制的意義不是很大(不排除個別現象)重復提交的控制也就主要是在插入時的控制了
  
  先說一下我們目前所做項目的情況
  
  目前的項目是用Struts+Spring+Ibatis頁面用jstlStruts復雜View層Spring在Service層提供事務控制Ibatis是用來代替JDBC所有頁面的訪問都不是直接訪問jsp而是訪問Structs的Action再由Action來Forward到一個Jsp所有針對數據庫的操作比如取數據或修改數據都是在Action裡面完成所有的Action一般都繼承BaseDispatchAction這個是自己建立的類目的是為所有的Action做一些統一的控制在Struts層對於一個功能我們一般分為兩個Action一個Action裡的功能是不需要調用Struts的驗證功能的(常見的方法名稱有addeditremoveviewlist)另一個是需要調用Struts的驗證功能的(常見的方法名稱有insertupdate)
  
  就拿論壇發貼來說吧論壇發貼首先需要跳轉到一個頁面你可以填寫帖子的主題和內容填寫完後單擊提交貼子就發表了所以這裡經過兩個步驟
  
  轉到一個新增的頁面在Action裡我們一般稱為add例如
  
  public ActionForward add(ActionMapping mapping ActionForm form
  
  HttpServletRequest request HttpServletResponse response)
  
  throws Exception {
  
  //這一句是輸出調試信息表示代碼執行到這一段了
  
  logdebug(:: action subject add);
  
  //your code here
  
  //這裡保存Token值
  
  saveToken(request);
  
  //跳轉到add頁面在Structsconfigxml裡面定義例如跳轉到subjectAddjsp
  
  return mappingfindForward(add);
  
  }
  
  在填寫標題和內容後選擇 提交 會提交到insert方法在insert方法裡判斷是否重復提交了
  
  public ActionForward insert(ActionMapping mapping ActionForm form
  
  HttpServletRequest request HttpServletResponse response){
  
  if (isTokenValid(request true)) {
  
  // 表單不是重復提交
  
  //這裡是保存數據的代碼
  
  } else {
  
  //表單重復提交
  
  saveToken(request);
  
  //其它的處理代碼
  
  }
  
  }
  
  下面更詳細一點(注意下面所有的代碼使用全角括號)
  
  你想發貼時點擊我要發貼鏈接的代碼可以裡這樣的
  
  〈html:link action=subjectdo?method=add〉我要發貼〈/html:link〉
  
  subjectdo 和 method 這些在structconfigxml如何定義我就不說了點擊鏈接後會執行subjectdo的add方法代碼如上面說的跳轉到subjectAddjsp頁面頁面的代碼大概如下
  
  〈html:form action=subjectFormdo?method=insert
  
  〈html:text property=title /〉
  
  〈html:textarea property=content /〉
  
  〈html:submit property=發表 /〉
  
  〈html:reset property=重填 /〉
  
  〈html:form〉
  
  如果你在add方法裡加了saveToken(request);這一句那在subjectAddjsp生成的頁面上會多一個隱藏字段類似於這樣〈input type=hidden name=orgapaclTOKEN value=aaffdcccae
  
  點擊發表後表單提交到subjectFormdo裡的insert方法後你在insert方法裡要將表單的數據插入到數據庫中如果沒有進行重復提交的控制那麼每點擊一次浏覽器的刷新按鈕都會在數據庫中插入一條相同的記錄增加下面的代碼你就可以控制用戶的重復提交了
  
  if (isTokenValid(request true)) {
  
  // 表單不是重復提交
  
  //這裡是保存數據的代碼
  
  } else {
  
  //表單重復提交
  
  saveToken(request);
  
  //其它的處理代碼
  
  }
  
  注意你必須在add方法裡使用了saveToken(request)你才能在insert裡判斷否則你每次保存操作都是重復提交
  
  記住一點Struts在你每次訪問Action的時候都會產生一個令牌保存在你的Session裡面如果你在Action裡的函數裡面使用了saveToken(request);那麼這個令牌也會保存在這個Action所Forward到的jsp所生成的靜態頁面裡
  
  如果你在你Action的方法裡使用了isTokenValid那麼Struts會將你從你的request裡面去獲取這個令牌值然後和Session裡的令牌值做比較如果兩者相等就不是重復提交如果不相等就是重復提交了
  
  由於我們項目的所有Action都是繼承自BaseDispatchAction這個類所以我們基本上都是在這個類裡面做了表單重復提交的控制默認是控制add方法和insert方法如果需要控制其它的方法就自己手動寫上面這些代碼否則是不需要手寫的控制的代碼如下
  
  public abstract class BaseDispatchAction extends BaseAction {
  
  protected ActionForward perform(ActionMapping mapping ActionForm form
  
  HttpServletRequest request HttpServletResponse response)
  
  throws Exception {
  
  String parameter = mappinggetParameter();
  
  String name = requestgetParameter(parameter);
  
  if (null == name) { //如果沒有指定 method 則默認為 list
  
  name = list;
  
  }
  
  if (addequals(name)) {
  
  if (addequals(name)) {
  
  saveToken(request);
  
  }
  
  } else if (insertequals(name)) {
  
  if (!isTokenValid(request true)) {
  
  resetToken(request);
  
  saveError(request new ActionMessage(errorrepeatSubmit));
  
  logerror(重復提交!);
  
  return mappingfindForward(error);
  
  }
  
  }
  
  return dispatchMethod(mapping form request response name);
  
  }
  
  }
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28119.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.