1. 程式人生 > >shiro原始碼篇 - shiro的session共享,你值得擁有

shiro原始碼篇 - shiro的session共享,你值得擁有

前言

  開心一刻

    老師對小明說:"乳就是小的意思,比如乳豬就是小豬,乳名就是小名,請你用乳字造個句"
    小明:"我家很窮,只能住在40平米的乳房"
    老師:"..., 這個不行,換一個"
    小明:"我每天上學都要跳過我家門口的一條乳溝"
    老師:"......, 這個也不行,再換一個"
    小明:"老師,我想不出來了,把我的乳頭都想破了!"

  路漫漫其修遠兮,吾將上下而求索!

  github:https://github.com/youzhibing

  碼雲(gitee):https://gitee.com/youzhibing

前情回顧

  

shiro的session建立session的查詢、更新、過期、刪除中,shiro對session的操作基本都講到了,但還缺一個session共享沒有講解;session共享的原理其實在自定義session管理一文已經講過了,本文不講原理,只看看shiro的session共享的實現。

  為何需要session共享

    如果是單機應用,那麼談不上session共享,session放哪都無所謂,不在乎放到預設的servlet容器中,還是抽出來放到單獨的地方;

    也就是說session共享是針對叢集(或分散式、或分散式叢集)的;如果不做session共享,仍然採用預設的方式(session存放到預設的servlet容器),當我們的應用是以叢集的方式釋出的時候,同個使用者的請求會被分發到不同的叢集節點(分發依賴具體的負載均衡規則),那麼每個處理同個使用者請求的節點都會重新生成該使用者的session,這些session之間是毫無關聯的。那麼同個使用者的請求會被當成多個不同使用者的請求,這肯定是不行的。

  如何實現session共享

    實現方式其實有很多,甚至可以不做session共享,具體有哪些,大家自行去查資料。本文提供一種方式:redis實現session共享,就是將session從servlet容器抽出來,放到redis中儲存,所有叢集節點都從redis中對session進行操作。

 

SessionDAO

  SessionDAO其實是用於session持久化的,但裡面有快取部分,具體細節我們往下看

  shiro已有SessionDAO的實現如下

  SessionDAO介面提供的方法如下

 View Code

    SessionDAO給出了從持久層(一般而言是關係型資料庫)操作session的標準。

  AbstractSessionDAO提供了SessionDAO的基本實現,如下

 View Code

    SessionDao的基本實現,實現了SessionDao的create、readSession(具體還是依賴AbstractSessionDAO子類的doCreate、doReadSession實現);同時加入了自己的sessionId生成器,負責sessionId的操作。

  CachingSessionDAO提供了session快取的功能,如下

 View Code

    是應用層與持久化層之間的快取層,不用頻繁請求持久化層以提升效率。重寫了AbstractSessionDAO中的create、readSession方法,實現了SessionDAO中的update、delete、getActiveSessions方法,預留doUpdate和doDelele給子類去實現(doXXX方法操作的是持久層)

  MemorySessionDAO,SessionDAO的簡單記憶體實現,如下

 View Code

    將session儲存在記憶體中,儲存結構是ConcurrentHashMap;專案中基本不用,即使我們不實現自己的SessionDAO,一般用的也是EnterpriseCacheSessionDAO。

  EnterpriseCacheSessionDAO,提供了快取功能的session維護,如下

 View Code

    設定了預設的快取管理器(AbstractCacheManager)和預設的快取例項(MapCache),實現了快取效果。從父類繼承的持久化操作方法(doXXX)都是空實現,也就說EnterpriseCacheSessionDAO是沒有實現持久化操作的,僅僅只是簡單的提供了快取實現。當然我們可以繼承EnterpriseCacheSessionDAO,重寫doXXX方法來實現持久化操作。

  總結下:SessionDAO定義了從持久層操作session的標準;AbstractSessionDAO提供了SessionDAO的基礎實現,如生成會話ID等;CachingSessionDAO提供了對開發者透明的session快取的功能,只需要設定相應的 CacheManager 即可;MemorySessionDAO直接在記憶體中進行session維護;而EnterpriseCacheSessionDAO提供了快取功能的session維護,預設情況下使用 MapCache 實現,內部使用ConcurrentHashMap儲存快取的會話。因為shiro不知道我們需要將session持久化到哪裡(關係型資料庫,還是檔案系統),所以只提供了MemorySessionDAO持久化到記憶體(聽起來怪怪的,記憶體中能說成持久層嗎)

 

shiro session共享

  共享實現

    shiro的session共享其實是比較簡單的,重寫CacheManager,將其操作指向我們的redis,然後實現我們自己的CachingSessionDAO定製快取操作和快取持久化。

    自定義CacheManager

      ShiroRedisCacheManager

 View Code

      ShiroRedisCache

 View Code

    自定義CachingSessionDAO

      繼承EnterpriseCacheSessionDAO,然後重新設定其CacheManager(替換掉預設的記憶體快取器),這樣也可以實現我們的自定義CachingSessionDAO,但是這是優選嗎;如若我們實現持久化,繼承EnterpriseCacheSessionDAO是優選,但如果只是實現session快取,那麼CachingSessionDAO是優選,自定義更靈活。那麼我們還是繼承CachingSessionDAO來實現我們的自定義CachingSessionDAO

      ShiroSessionDAO

 View Code

    最後將ShiroSessionDAO例項賦值給SessionManager例項,再講SessionManager例項賦值給SecurityManager例項即可

    具體程式碼請參考spring-boot-shiro

  原始碼解析

    底層還是利用Filter + HttpServletRequestWrapper將對session的操作接入到自己的實現中來,而不走預設的servlet容器,這樣對session的操作完全由我們自己掌握。

    shiro的session建立中其實講到了shiro中對session操作的基本流程,這裡不再贅述,沒看的朋友可以先去看看再回過頭來看這篇。本文只講shiro中,如何將一個請求的session接入到自己的實現中來的;shiro中有很多預設的filter,我會單獨開一篇來講shiro的filter,這篇我們先不糾結這些filter。

    OncePerRequestFilter中doFilter方法如下

 View Code

    上圖中,我可以看到AbstractShiroFilter的doFilterInternal放中將request封裝成了shiro自定義的ShiroHttpServletRequest,將response也封裝成了shiro自定義的ShiroHttpServletResponse。既然Filter中將request封裝了ShiroHttpServletRequest,那麼到我們應用的request就是ShiroHttpServletRequest型別,也就是說我們對session的操作最終都是由shiro完成的,而不是預設的servlet容器。

    另外補充一點,shiro的session建立不是懶建立的。servlet容器中的session建立是第一次請求session(第一呼叫request.getSession())時才建立。shiro的session建立如下圖

    此時,還沒登入,但是subject、session已經建立了,只是subject的認證狀態為false,說明還沒進行登入認證的。至於session建立過程已經儲存到redis的流程需要大家自行去跟,或者閱讀我之前的博文

 

總結

  1、當以叢集方式對外提供服務的時候,不做session共享也是可以的

    可以通過ip_hash的機制將同個ip的請求定向到同一臺後端,這樣保證使用者的請求始終是同一臺服務處理,與單機應用基本一致了;但這有很多方面的缺陷(具體就不詳說了),不推薦使用。

  2、servlet容器之間做session同步也是可以實現session共享的

    一個servlet容器生成session,其他節點的servlet容器從此servlet容器進行session同步,以達到session資訊一致。這個也不推薦,某個時間點會有session不一致的問題,畢竟同步過程受到各方面的影響,不能保證session實時一致。

  3、session共享實現的原理其實都是一樣的,都是filter + HttpServletRequestWrapper,只是實現細節會有所區別;有興趣的可以看下spring-session的實現細節。

  4、如果我們採用的spring整合shiro,其實可以將快取管理器交由spring管理,相當於由spring統一管理快取。

  5、shiro的CacheManager不只是管理session快取,還管理著身份認證快取、授權快取,shiro的快取都是CacheManager管理。但是身份認證快取預設是關閉的,個人也不推薦開啟。

  6、shiro的session建立時機是在登入認證之前,而不是第一次呼叫getSession()時。

 

參考

  《跟我學shiro》

來源:https://www.cnblogs.com/youzhibing/p/9749427.html

 

鄭州婦科醫院哪家好

鄭州無痛人流醫院

鄭州治療婦科哪家醫院好

鄭州專業婦科醫院