Token(令牌)防止表單重複提交

若是表單能夠重複提交,會給服務器帶來一些沒必要要的麻煩。也許,您會說,只要咱們把提交的Action隱藏就能夠了。例如在Struts中,只須要在Forward的後面加入一個屬性redirect="true"就能夠了。這個辦法雖然看不見那個*.do了,可是別人可使用後退,等退到剛纔的頁面,繼續點提交,因此這方法不可行。
不過剛纔在網上還看了一下資料,網上說有三中方法。
第一個是在javascipt中設置一個變量。 javascript



第二個是在第一次提交完成,給提交按鈕加屬性disabled html



注意:這個disabled屬性值在JS中應該是disabled或者" ",而在C#中,值纔是true或者false,不要搞渾了哦!~
你們想一想,這可都是javascript,能夠說是在當前頁面有效的。若是我提交之後,跳到別的頁面,而後我在後退回來,頁面仍是要從新加載,那麼咱們的變量不就白設置。難道說這兩種方法就不行了嗎?不能這樣說,既然人家寫出來確定有人家的道理,只不過咱們用錯地方了。您想一下,什麼提交會在一個頁面完成?對AJAX,因此說,這兩個方法,只能應用雜AJAX提交中。 java


真正能解決問題的,固然就是咱們今天的重點--->Token(令牌)
在Struts中,咱們只須要調用幾個方法,就能夠完成。先來看看我是如何作的,而後我說下原理。
先來作一個Action(TokenAction),在這個Action中生成一個令牌。 apache



而後跳轉到test4.jsp頁面,執行咱們的提交操做。 服務器



提交進入LoginAction,在這個Action中作判斷。 session



這樣就提交成功了。這很正常。 app



如今咱們點後退,在提交一下。 jsp


到這裏,咱們的提交問題就算解決了。怎麼樣,很簡單吧。畢竟Struts已經給寫好了,咱們只須要調用就能夠了。
不過須要注意的一點,在提交的時候,要用<html:form>,若是用通常的From標籤,就跳不到提交成功頁面(究竟是爲何,立刻就會講到!)
看到這裏,您會問了,咱們剛纔就調用了,那幾個方法,但是內部怎樣實現的咱們一點都不知道啊!~別急,等我慢慢說來。
Step 1:
先來看看第一個方法saveToken()
public synchronized void saveToken(HttpServletRequest request) {
    HttpSession session = request.getSession();
    String token = generateToken(request);
    if (token != null) {
        session.setAttribute(Globals.TRANSACTION_TOKEN_KEY, token);
    }
}
看來他是把值放入Session中了,並且命名爲Globals.TRANSACTION_TOKEN_KEY
Step 2:
protected String renderToken() {
    StringBuffer results = new StringBuffer();
    HttpSession session = pageContext.getSession();
    if (session != null) {
        String token =
            (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
        if (token != null) {
            results.append("<input type=\"hidden\" name=\"");
            results.append(Constants.TOKEN_KEY);
            results.append("\" value=\"");
            results.append(token);
            if (this.isXhtml()) {
                results.append("\" />");
            } else {
                results.append("\">");
            }
        }
    }
    return results.toString();
}
剛纔,還記得,我在上面說到一個注意,說是要用<html:from>緣由就在這第二步。由於當應用服務器初始化test4.jsp頁面遇到標籤<html:form>時,便會調用struts的FormTag類的renderToken()方法。
第二步的意思是:當檢測到session中的Globals.TRANSACTION_TOKEN_KEY不爲空時,在test4.jsp頁面建立元素:
<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="">,
名稱爲:org.apache.struts.taglib.html.TOKEN就是Constants.TOKEN_KEY;
值爲:session中的Globals.TRANSACTION_TOKEN_KEY的值,即爲同步令牌值。
step 3:
取得session中的令牌值,而後resetToken,再從頁面hidden元素取來令牌值,進行比較,若是相等則爲第一次,不等則爲重複提交。
public synchronized boolean isTokenValid(
    HttpServletRequest request,
    boolean reset) {
    // Retrieve the current session for this request
    HttpSession session = request.getSession(false);
    if (session == null) {
        return false;
    }
    // Retrieve the transaction token from this session, and
    // reset it if requested
    String saved = (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
    if (saved == null) {
        return false;
    }
    if (reset) {
        this.resetToken(request);
    }
    // Retrieve the transaction token included in this request
    String token = request.getParameter(Constants.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(Globals.TRANSACTION_TOKEN_KEY);
}
所謂知其然,還要知其因此然,這樣才能來去自如嘛,嘿嘿!~ this

相關文章
相關標籤/搜索