分佈式cookie-session的實現(spring-session)

1     session存儲策略html

存儲,即在後臺使用session的setAttribute,getAttribute等方法時,這些內部存放的數據最終存儲至什麼位置。好比在默認的tomcat實現中,相應的數據即存儲在內存中,並在中止以後會序列化至磁盤中。
可使用內存存儲和第三方存儲,如redis。對於集羣式的session存儲,那麼確定會使用第三方存儲的實現。html5

在spring-session中,對session存儲單獨做了一個定義,但定義上基本保證與http session一致,主要的目的在於它能夠支持在非http的環境中模擬使用。所以不直接使用http session接口。
先看定義:web

public interface Session {
      /** 獲取唯一的id,能夠理解爲即將每一個session看成一個實體對象 */
     String getId();
    <T> T getAttribute(String attributeName);
     Set<String> getAttributeNames();
     void setAttribute(String attributeName, Object attributeValue);
     void removeAttribute(String attributeName);
}

從這個定義來看,若是要使用httpSession,若是實現了這個session接口,那麼實現上就能夠所有實現httpSession對於存儲的要求。對於非httpSession場景,使用這個也能夠達到session存儲的目的。
固然,爲了保證在會話場景中會使用到失效,最後訪問時間,最大不活躍時間的目的,spring-session也有一個繼承於session接口的expiringSession接口。redis

1.0    id主鍵的生成spring

對於id,即理解爲session實體對象唯一鍵,能夠採用任意的一種唯一key生成策略。好比,使用uuid來生成唯一鍵。同時,也能夠將這個id認爲就是在http中sessionCookie的值後端

1.1     map存儲瀏覽器

若是是map來存儲,那麼 set,get,remove就能夠直接使用map的相應實現便可。在spring-session實現中MapSession,即採用內部的一個hashmap來進行存儲。tomcat

1.2     redis存儲安全

在sprng-session中,將屬性的存儲和總體的存儲分開,使用專門的倉庫來處理session實體的處理。對於從領域模型的概念來 說,set,get,removeAttribute只是對屬性的處理,處理的是一個內部狀態的變化,對於總體的實體來講,並無總體上的處理。具體到實 現,在redis的存儲實現中,spring-session內部使用了一個 mapSession來存儲相應的屬性信息。
在一個實體被建立以後,相應的屬性信息被所有設置至mapSession中,而後接下來的屬性訪問均經過mapSession來處理。爲了最終存儲相應的 數據值,redis實現使用了一個額外的deltaMap來存放變化了的數據,並在從新保存時,一次性地將全部屬性put至redis中。
從這個實現來看,spring-session的實現並非實時地與redis進行交互,這種實現方式在解決同一session設置衝突屬性時可能存在問題。所以存在這種場景的,須要從新實現相應的邏輯。服務器

2     session倉庫策略

之因此將存儲策略和倉庫策略分開,也是spring-session的一個設計思想。spring將session設計爲一個實體,所以將實體對象的操做 和屬性的操做進行拆分。在前面的存儲策略已經描述了屬性的操做,所以這裏的倉庫策略主要處理實體的建立,獲取,更新,刪除等,即CRUD。以下的定義所 示:

public interface SessionRepository<S extends Session> {
      /** 新建 */
     S createSession();
      /** 保存或更新 */
     void save(S session);
      /** 獲取 */
     S getSession(String id);
      /** 刪除 */
     void delete(String id);
}

2.1     本地倉庫
若是是本地倉庫,那麼可使用一個本地全局的globalMap,而後使用sessionId做爲key,mapSession做爲value來實現便可。

2.2     redis倉庫

若是是resi倉庫,那麼在新建時,只是在本地建立一個redisSession對象,而後在redis存儲中使用一個 hash結構來進行存儲。即<I,<K,V>>的結構。其中I即表示sessionId,<k,V>表示具體的 redisSession對象。其中getSession,delete都是針對I的操做,而save則在在指定I的狀況下,直接保 持<K,V>便可。

爲了在必定程度上進行優化,以及解決在必定程序上的覆蓋問題,在redis實現時。spring-session只對修改過的屬性做存儲,即當使用 setAttribute處理屬性時,spring-session使用額外的delta來保存修改過的屬性,最後進行save時,只操做這部分數據即 可。而且redis也支持調用hSet來保存或更新修改過的數據。

3     session傳輸策略

3.1     cookie傳輸

常規的實現也是使用cookie傳輸,好比tomcat,jetty等。在這種狀況下,後端在建立session時,若是這是新的session,會自動 調用原生response.addCookie以建立一個新的cookie信息。爲保證安全,採用httpOnly進行建立。在發送給客戶端以後的每次交 互,瀏覽器會自動將這些信息傳輸至服務器端。而後服務器端直接從cookie中獲取到sessionId,再進一步操做便可。
對於這種實現,應用程序開發不須要做任何的調整,瀏覽器會自動處理cookie的傳輸。

3.2     header傳輸

header傳輸,即在建立新session時,往response header中使用addHeader添加一個新的響應頭,如session頭,headerValue中存放相應的sessionId值。
在客戶端實現中,客戶端代碼須要手動地將響應值進行記錄,並在請求時加入到請求頭當中。而且須要監聽響應頭值的變化,好比sessionId值的變化處理。相比cookie傳輸來講,這種實現對於開發更復雜一點。

4     http協議兼容

4.1     爲保證http協議兼容,在session信息準備好以後,其必須在響應頭以後進行處理

不論是使用cookie傳輸仍是header傳輸,在響應時都須要保證在響應body輸出時,相應的頭信息都應該先被處理。(cookie也是一種特殊的header)。若是像如下的代碼,就不能保證session響應被正確地處理

X implements Filter {
      filterChain.doFilter(request,response)
      // do session logic
}

在這種實現中,若是在應用代碼中直接使用response的outputStream進行輸出時,這裏的過濾器實現中的do Session操做就不能實現相應的邏輯。由於http協議中的消息機制已經再也不容許再輸出body以後輸出header信息了。

在spring-session的實現中,咱們只須要保證在調用response處理響應body時,可以處理session邏輯便可。那麼可使用一種 hack的技術,針對在response輸出時,斷定是否在處理body或應該結束響應時,加入特定的hack以處理咱們的do session操做。所以提供了 OnCommittedResponseWrapper 以便在特定的時機進行處理,在具體的 onResponseCommitted中,處理session的delete響應和save等便可。

4.2     爲支持多瀏覽器訪問,所以在特定的路徑信息處理時,應當自動地處理相應的browser參數

關於多browser支持,能夠先看看此篇文章。
http://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-multi

在多browser支持時,主要即在請求參數中增長相應的瀏覽器識別參數_s,那麼在須要進行界面跳轉或其它地址處理時,後臺應該可以自動地處理相應的參 數信息。在spring-session中,爲了對應用程序透明,經過重寫response的encodeURL和encodeRedirectURL 時,增長對相應_s的處理即達到相應的處理透明便可。

相關文章
相關標籤/搜索