日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

javaEE開發(fā)中使用session同步和token機(jī)制來防止并發(fā)重復(fù)提交 | i fly...

 hmtomyang 2013-03-14

javaEE開發(fā)中使用session同步和token機(jī)制來防止并發(fā)重復(fù)提交

2011/07/14 10:48:42 No Comments

    通常在普通的操作當(dāng)中,我們不需要處理重復(fù)提交的,而且有很多方法來防止重復(fù)提交。比如在登陸過程中,通過使用redirect,可以讓用戶登陸之上重定向到后臺首頁界面,當(dāng)用戶刷新界面時就不會觸發(fā)重復(fù)提交了?;蛘呤褂胻oken,隱藏在表單中,當(dāng)提交時進(jìn)行token驗證,驗證失敗也不讓提交。這都是一般的做法。

    我們這次碰到的問題是重復(fù)提交本身就是一個錯誤,重復(fù)提交會導(dǎo)致一些相關(guān)數(shù)據(jù)的邏輯不再正確。而這些重復(fù)提交并不是通過普通的刷新界面,或者兩次點(diǎn)擊按鈕來進(jìn)行的。在普通的操作當(dāng)中,我們可以通過一系列的手段,使得相應(yīng)參數(shù)被清零,從而防止數(shù)據(jù)上的不正確。但是,在一種情況下,這些手段都不再有效,那就是并發(fā)的重復(fù)提交。
    并發(fā)重復(fù)提交,那就是在同一時間內(nèi)(時間間隔可以縮短到0.X秒之內(nèi)),在這種情況下,所有的常規(guī)邏輯都不再有效,因為多個請求,同時進(jìn)入系統(tǒng),系統(tǒng)已不能判斷出這些請求是否是無效的,它們同時通過常規(guī)的重復(fù)邏輯判斷,并最終在同一時間內(nèi)將數(shù)據(jù)寫入到數(shù)據(jù)庫中,引起數(shù)據(jù)錯誤。

    舉一個簡單的例子,在系統(tǒng)中銷售一個商品,首先通過該商品id進(jìn)入到系統(tǒng)邏輯判斷,判斷此商品是否已售出,如果未售出,就進(jìn)行數(shù)據(jù)存取操作。商品是否售出,是一個邏輯判斷,是驗證數(shù)據(jù)存儲到數(shù)據(jù)庫的一道門。在常規(guī)的判斷當(dāng)中,前一請求通過這道門之后,后一請求就不能通過了,因為驗證為false。但在并發(fā)請求中,兩個或多個請求同時通過了這道門,因為都是同時進(jìn)入到判斷,在判斷之前都驗證商品沒有被售出,所以就同時進(jìn)入到數(shù)據(jù)的存儲當(dāng)中。

    在常規(guī)的java開發(fā)中,對于這種情況,臨界資源,通常是使用加鎖來保證這種情況的先后順序。但是加鎖有一個問題即是,它是對于全局信息的加鎖,即對整個將要銷售的商品進(jìn)行加鎖了。對于BS應(yīng)用來說,我們必須保證另一個操作人員的同一種商品的銷售請求通過,即只限制同一個操作人員銷售的并發(fā)請求,不限制多個操作人員不同請求的處理。
    在這種情況下,我們的加鎖就不能簡單的鎖定在商品上,而是要鎖定在與操作人員有關(guān)的信息上,這就是session。

    session是一個在單個操作人員整個操作過程中,與服務(wù)器端保持通信的惟一識別信息。在同一操作人員的多次請求當(dāng)中,session始終保證是同一個對象,而不是多個對象,因為可以對其加鎖。當(dāng)同一操作人員多個請求進(jìn)入時,可以通過session限制只能單向通行。
    本文正是通過使用session以及在session中加入token,來驗證同一個操作人員是否進(jìn)行了并發(fā)重復(fù)的請求,在后一個請求到來時,使用session中的token驗證請求中的token是否一致,當(dāng)不一致時,被認(rèn)為是重復(fù)提交,將不準(zhǔn)許通過。
    整個流程可以由如下流程來表述:

  1. 客戶端申請token
  2. 服務(wù)器端生成token,并存放在session中,同時將token發(fā)送到客戶端
  3. 客戶端存儲token,在請求提交時,同時發(fā)送token信息
  4. 服務(wù)器端統(tǒng)一攔截同一個用戶的所有請求,驗證當(dāng)前請求是否需要被驗證(不是所有請求都驗證重復(fù)提交)
  5. 驗證session中token是否和用戶請求中的token一致,如果一致則放行
  6. session清除會話中的token,為下一次的token生成作準(zhǔn)備
  7. 并發(fā)重復(fù)請求到來,驗證token和請求token不一致,請求被拒絕

    由以上的流程,我們整個實(shí)現(xiàn)需要以下幾個東西

  1. token生成器,負(fù)責(zé)生成token
  2. 客戶token請求處理action,負(fù)責(zé)處理客戶請求,并返回token信息
  3. token攔截器,用于攔截指定的請求是否需要驗證token
  4. token請求攔截標(biāo)識,用于標(biāo)識哪些請求是需要被攔截的
  5. 客戶端token請求處理方法,用于請求token,并存放于特定操作中,并在提交時發(fā)送到請求中

    token生成器
    token生成器在這里使用了一個隨機(jī)數(shù)來實(shí)現(xiàn),即隨機(jī)生成一個數(shù)字,即實(shí)現(xiàn)token生成,如下所示:

1
2
3
4
5
6
7
8
9
private static final Random random = new Random(System.currentTimeMillis());
public static final String TOKENPARAM = "session-token";
 
/** 生成一個token */
public static synchronized String generateToken(HttpSession session) {
    String s = String.valueOf(random.nextLong());
    session.setAttribute(TOKENPARAM, s);
    return s;
}

    token請求處理action
    請求處理action,即接收相應(yīng)的請求,然后直接返回相對應(yīng)的token即可,如下即為一個為ajax請求生成token的處理action:

1
2
3
4
5
public String generateTokenAjax() {
    String token = SessionTokenGenerator.generateToken(ServletActionContext.getRequest().getSession());
    AjaxSupport.sendSuccessText(token);
    return NONE;
}

    token請求攔截標(biāo)識
    攔截標(biāo)識,即表示哪些方法需要被攔截,這里可以使用注解來實(shí)現(xiàn),即在要攔截的方法上追加類似@TokenNeed的注解,或者使用配置文件,將需要攔截的方法列表記錄在配置文件中,在本文中,使用了一個配置文件來記錄

    token攔截器
    token攔截器實(shí)現(xiàn)了我們所需要的攔截處理,在當(dāng)碰到需要攔截的方法請求中,將同步進(jìn)行token的判斷和處理,并根據(jù)處理結(jié)果判斷是否該繼續(xù)放行或攔截之:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public String intercept(ActionInvocation invocation) throws  Exception {
    String action = invocation.getProxy().getAction().getClass().getName();
    String method = invocation.getProxy().getMethod();
    final HttpSession session = ServletActionContext.getRequest().getSession();
    if(includeMethodSet.contains(action + "." + method)) {
        synchronized(session) {
            String paramSessionToken = ServletActionContext.getRequest().getParameter(SessionTokenGenerator.TOKENPARAM);
            String sessionSessionToken = (String) session.getAttribute(SessionTokenGenerator.TOKENPARAM);
            if(sessionSessionToken == null || paramSessionToken == null || !paramSessionToken.equals(sessionSessionToken))
                return fail();
            session.removeAttribute(SessionTokenGenerator.TOKENPARAM);
        }
    }
    return invocation.invoke();
}

    如上即是判斷處理的方法是否在攔截列表中,如果是,則取得參數(shù)中的token,再將其與session中的token相比,如果不一致,則直接返回fail,隨后將其從session中移除。

    客戶端token實(shí)現(xiàn)
    作為客戶端,只需要在進(jìn)行請求提交之前申請一個token,在請求時,將此token加到請求中即可。在本文中,有一個jquery的ajax方法來處理token請求,隨后在進(jìn)行ajax請求時將此token一起加入到param。如下即為token的jquery請求

1
2
3
4
5
6
m_ylf.token = function() {
    m_ylf.invoke("/token/generateToken",{}, function(re) {
        re = re["result"];
        window["session-token"] = re;
    });
}

    即在處理時將接收到的token放到window中,要提交請求時再將其從window中取出,一并提交即可,如下的統(tǒng)一ajax處理方法:

1
2
3
//追加session-token
if(window["session-token"])
    param["session-token"] = window["session-token"];

    至此,整個防session Token請求即完成。如果在客戶端模擬多個請求中,首先會有一個請求被成功處理,其它的請求即直接返回類似“不能重復(fù)提交”的錯誤警告(對于ajax請求)。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多