開心一刻html
開學了,表弟和同窗由於打架,老師讓他回去叫家長。表弟硬氣的說:不用,我打得過他。老師板着臉對他說:和你打架的那位同窗已經回去叫家長了。表弟猶豫了一會依然硬氣的說:能夠,兩個我也打得過。老師:......java
路漫漫其修遠兮,吾將上下而求索!git
github:https://github.com/youzhibinggithub
碼雲(gitee):https://gitee.com/youzhibingweb
你們還記得上篇博文講了什麼嗎,咱們來一塊兒簡單回顧下:spring
HttpServletRequestWrapper是HttpServletRequest的裝飾類,咱們經過繼承HttpServletRequestWrapper來實現咱們自定義的HttpServletRequest:CustomizeSessionHttpServletRequest,重寫CustomizeSessionHttpServletRequest的getSession,將其指向咱們自定義的session。而後經過Filter將CustomizeSessionHttpServletRequest添加到Filter chain中,使獲得達Servlet的ServletRequest是咱們的CustomizeSessionHttpServletRequest。安全
今天不講session共享,咱們先來看看shiro的session建立cookie
SecurityManager,安全管理器;即全部與安全相關的操做都會與SecurityManager交互;它管理着全部Subject,全部Subject都綁定到SecurityManager,與Subject的全部交互都會委託給SecurityManager;SecurityManager是shiro的核心,它負責與shiro的其餘組件進行交互,相似SpringMVC中的DispatcherServlet或Struts2中的FilterDispatcher。session
咱們在使用shiro的時候,首先都會先初始化SecurityManager,而後往SecurityManager中注入shiro的其餘組件,像sessionManager、realm等。咱們的spring-boot-shiro中初始化的是DefaultWebSecurityManager,以下app
@Bean public SecurityManager securityManager(AuthorizingRealm myShiroRealm, CacheManager shiroRedisCacheManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setCacheManager(shiroRedisCacheManager); securityManager.setRememberMeManager(cookieRememberMeManager()); securityManager.setRealm(myShiroRealm); return securityManager; }
結構以下,認真看看,注意看下屬性
頂層組件SecurityManager直接繼承了SessionManager且提供了SessionsSecurityManager實現,SessionsSecurityManager直接把會話管理委託給相應的SessionManager;SecurityManager的默認實現:DefaultSecurityManager及DefaultWebSecurityManager都繼承了SessionsSecurityManager,也就是說:默認狀況下,session的管理由DefaultSecurityManager或DefaultWebSecurityManager中的SessionManager來負責。
默認安全管理器,用於咱們的javaSE安全管理,通常而言用到的少,但咱們須要記住,萬一哪次有這個需求呢。
咱們來看下他的構造方法
默認的sessionManager是DefaultSessionManager,DefaultSessionManager具體詳情請看下文。
默認web安全管理器,用於咱們的web安全管理;通常而言,咱們的應用中初始化此安全管理器。
咱們來看看其構造方法
public DefaultWebSecurityManager() { super(); // 會調用SessionsSecurityManager的構造方法,實例化DefaultSessionManager ((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator()); this.sessionMode = HTTP_SESSION_MODE; setSubjectFactory(new DefaultWebSubjectFactory()); setRememberMeManager(new CookieRememberMeManager()); setSessionManager(new ServletContainerSessionManager()); // 設置sessionManager,替換掉上面的DefaultSessionManager }
能夠看出此時的sessionManager是ServletContainerSessionManager,ServletContainerSessionManager具體詳情請看下文。
由此可知默認狀況下,DefaultSecurityManager會將session管理委託給DefaultSessionManager,而DefaultWebSecurityManager則將session管理委託給ServletContainerSessionManager。
咱們能夠經過繼承DefaultSecurityManager或DefaultWebSecurityManager來實現自定義SecurityManager,但通常而言不必,DefaultSecurityManager和DefaultWebSecurityManager基本能知足咱們的須要了,咱們根據需求二選其一便可。不管DefaultSecurityManager仍是DefaultWebSecurityManager,咱們均可以經過setSessionManager方法來指定sessionManager,若是不指定sessionManager的話就用的SecurityManager默認的sessionManager。
shiro提供了完整的會話管理功能,不依賴底層容器,JavaSE應用和JavaEE應用均可以使用。會話管理器管理着應用中全部Subject的會話,包括會話的建立、維護、刪除、失效、驗證等工做。
DefaultSecurityManager默認使用的SessionManager,用於JavaSE環境的session管理。
經過上圖可知(結合SecurityManager類圖),session建立的關鍵入口是SessionsSecurityManager的start方法,此方法中會將session的建立任務委託給具體的SessionManager實現。
DefaultSessionManager繼承自AbstractNativeSessionManager,沒用重寫start方法,因此此時AbstractNativeSessionManager的start方法會被調用,start方法以下
public Session start(SessionContext context) { Session session = createSession(context); // 建立session,類型是SimpleSession applyGlobalSessionTimeout(session); // 設置session的timeout,也就是有效時間,默認30分鐘 onStart(session, context); notifyStart(session); // 通知session監聽器 //Don't expose the EIS-tier Session object to the client-tier: return createExposedSession(session, context); // 建立對外暴露的session,SimpleSession的代理;類型是DelegatingSession,持有sessionManager的引用 }
跟進createSession(),代碼入下
protected Session createSession(SessionContext context) throws AuthorizationException { enableSessionValidationIfNecessary(); // 初次被調用時啓動定時任務來驗證session,具體請看下篇博客 return doCreateSession(context); // 真正建立session }
其中doCreateSession方法完成session的建立,doCreateSession方法你們能夠自行去跟下,我在這總結一下:
建立session,並生成sessionId,session是shiro的SimpleSession類型,sessionId採用的是隨機的UUID字符串;
sessionDAO類型是MemorySessionDAO,session存放在sessionDAO的private ConcurrentMap<Serializable, Session> sessions;屬性中,key是sessionId,value是session對象;
除了MemorySessionDAO,shiro還提供了EnterpriseCacheSessionDAO,具體二者有啥區別請看個人另外一篇博客講解。
DefaultWebSecurityManager默認使用的SessionManager,用於Web環境,直接使用的Servlet容器的會話,具體實現咱們往下看。
ServletContainerSessionManager實現了SessionManager,並重寫了SessionManager的start方法,那麼咱們從ServletContainerSessionManager的start方法開始來看看session的建立過程,以下圖
shiro有本身的HttpServletSession,HttpServletSession持有servlet的HttpSession的引用,最終對HttpServletSession的操做都會委託給HttpSession(裝飾模式)。那麼此時的session是標準servlet容器支持的HttpSession實例,它不與Shiro的任何與會話相關的組件(如SessionManager,SecurityManager等)交互,徹底由servlet容器管理。
用於Web環境,能夠替換ServletContainerSessionManager,廢棄了Servlet容器的會話管理;經過此能夠實現咱們本身的session管理;
從SessionManager類圖可知,DefaultWebSessionManager繼承自DefaultSessionManager,也沒有重寫start方法,那麼建立過程仍是沿用的AbstractNativeSessionManager的start方法;若是咱們沒有指定本身的sessionDao,那麼session仍是存在MemorySessionDAO的ConcurrentMap<Serializable, Session> sessions中,具體能夠看上述中的DefaultSessionManager。
經過DefaultWebSessionManager實現session共享,請點此處!
SecurityManager和SessionManager的類圖須要認真看看;
Subject的全部交互都會委託給SecurityManager;SecurityManager是shiro的核心,它負責與shiro的其餘組件進行交互,相似SpringMVC中的DispatcherServlet或Struts2中的FilterDispatcher;
SecurityManager會將session管理委託給SessionManager;SessionsSecurityManager的start方法中將session的建立委託給了具體的sessionManager,是建立session的關鍵入口。
shiro的SimpleSession與HttpServletSession
HttpServletSession只是servlet容器的session的裝飾,最終仍是依賴servlet容器,是shiro對servlet容器的session的一種支持;
而SimpleSession是shiro完徹底全的本身實現,是shiro對session的一種拓展。但SimpleSession不對外暴露,咱們通常操做的是SimpleSession的代理:DelegatingSession,或者是DelegatingSession的代理:StoppingAwareProxiedSession;對session的操做,會經過一層層代理,來到DelegatingSession,DelegatingSession將session的操做轉交給sessionMananger,sessionManager經過一些校驗後,最後轉交給SimpleSession處理。
《跟我學shiro》