<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                Shiro 提供了完整的企業級會話管理功能,不依賴于底層容器(如 web 容器 tomcat),不管 JavaSE 還是 JavaEE 環境都可以使用,提供了會話管理、會話事件監聽、會話存儲 / 持久化、容器無關的集群、失效 / 過期支持、對 Web 的透明支持、SSO 單點登錄的支持等特性。即直接使用 Shiro 的會話管理可以直接替換如 Web 容器的會話管理。 ## 10.1 會話 所謂會話,即用戶訪問應用時保持的連接關系,在多次交互中應用能夠識別出當前訪問的用戶是誰,且可以在多次交互中保存一些數據。如訪問一些網站時登錄成功后,網站可以記住用戶,且在退出之前都可以識別當前用戶是誰。 Shiro 的會話支持不僅可以在普通的 JavaSE 應用中使用,也可以在 JavaEE 應用中使用,如 web 應用。且使用方式是一致的。 ``` java login("classpath:shiro.ini", "zhang", "123"); Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession(); ``` 登錄成功后使用 Subject.getSession() 即可獲取會話;其等價于 Subject.getSession(true),即如果當前沒有創建 Session 對象會創建一個;另外 Subject.getSession(false),如果當前沒有創建 Session 則返回 null(不過默認情況下如果啟用會話存儲功能的話在創建 Subject 時會主動創建一個 Session)。 `session.getId();` 獲取當前會話的唯一標識。 `session.getHost();` 獲取當前 Subject 的主機地址,該地址是通過 HostAuthenticationToken.getHost() 提供的。 ``` java session.getTimeout(); session.setTimeout(毫秒); ``` 獲取 / 設置當前 Session 的過期時間;如果不設置默認是會話管理器的全局過期時間。 ``` java session.getStartTimestamp(); session.getLastAccessTime(); ``` 獲取會話的啟動時間及最后訪問時間;如果是 JavaSE 應用需要自己定期調用 session.touch() 去更新最后訪問時間;如果是 Web 應用,每次進入 ShiroFilter 都會自動調用 session.touch() 來更新最后訪問時間。 ``` java session.touch(); session.stop(); ``` 更新會話最后訪問時間及銷毀會話;當 Subject.logout() 時會自動調用 stop 方法來銷毀會話。如果在 web 中,調用 javax.servlet.http.HttpSession. invalidate() 也會自動調用 Shiro Session.stop 方法進行銷毀 Shiro 的會話。 ``` java session.setAttribute("key", "123"); Assert.assertEquals("123", session.getAttribute("key")); session.removeAttribute("key"); ``` 設置 / 獲取 / 刪除會話屬性;在整個會話范圍內都可以對這些屬性進行操作。 Shiro 提供的會話可以用于 JavaSE/JavaEE 環境,不依賴于任何底層容器,可以獨立使用,是完整的會話模塊。 ## 10.2 會話管理器 會話管理器管理著應用中所有 Subject 的會話的創建、維護、刪除、失效、驗證等工作。是 Shiro 的核心組件,頂層組件 SecurityManager 直接繼承了 SessionManager,且提供了SessionsSecurityManager 實現直接把會話管理委托給相應的 SessionManager,DefaultSecurityManager 及 DefaultWebSecurityManager 默認 SecurityManager 都繼承了 SessionsSecurityManager。 SecurityManager 提供了如下接口: ``` java Session start(SessionContext context); //啟動會話 Session getSession(SessionKey key) throws SessionException; //根據會話Key獲取會話 ``` 另外用于 Web 環境的 WebSessionManager 又提供了如下接口: `boolean isServletContainerSessions();// 是否使用 Servlet 容器的會話` Shiro 還提供了 ValidatingSessionManager 用于驗資并過期會話: `void validateSessions();// 驗證所有會話是否過期` ![](https://box.kancloud.cn/36f7e5eb35eafba2999eb569319bb5f1_806x413.png) Shiro 提供了三個默認實現: **DefaultSessionManager**:DefaultSecurityManager 使用的默認實現,用于 JavaSE 環境; **ServletContainerSessionManager**:DefaultWebSecurityManager 使用的默認實現,用于 Web 環境,其直接使用 Servlet 容器的會話; **DefaultWebSessionManager**:用于 Web 環境的實現,可以替代 ServletContainerSessionManager,自己維護著會話,直接廢棄了 Servlet 容器的會話管理。 替換 SecurityManager 默認的 SessionManager 可以在 ini 中配置(shiro.ini): ``` ini [main] sessionManager=org.apache.shiro.session.mgt.DefaultSessionManager securityManager.sessionManager=$sessionManager ``` Web 環境下的 ini 配置 (shiro-web.ini): `<!--EndFragment-->` ``` ini [main] sessionManager=org.apache.shiro.web.session.mgt.ServletContainerSessionManager securityManager.sessionManager=$sessionManager ``` 另外可以設置會話的全局過期時間(毫秒為單位),默認 30 分鐘: `sessionManager. globalSessionTimeout=1800000` 默認情況下 globalSessionTimeout 將應用給所有 Session。可以單獨設置每個 Session 的 timeout 屬性來為每個 Session 設置其超時時間。 另外如果使用 ServletContainerSessionManager 進行會話管理,Session 的超時依賴于底層 Servlet 容器的超時時間,可以在 web.xml 中配置其會話的超時時間(分鐘為單位): ``` xml <session-config> <session-timeout>30</session-timeout> </session-config> ``` 在 Servlet 容器中,默認使用 JSESSIONID Cookie 維護會話,且會話默認是跟容器綁定的;在某些情況下可能需要使用自己的會話機制,此時我們可以使用 DefaultWebSessionManager 來維護會話: ``` ini sessionIdCookie=org.apache.shiro.web.servlet.SimpleCookie sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager sessionIdCookie.name=sid #sessionIdCookie.domain=sishuok.com #sessionIdCookie.path= sessionIdCookie.maxAge=1800 sessionIdCookie.httpOnly=true sessionManager.sessionIdCookie=$sessionIdCookie sessionManager.sessionIdCookieEnabled=true securityManager.sessionManager=$sessionManager ``` * sessionIdCookie 是 sessionManager 創建會話 Cookie 的模板: * sessionIdCookie.name:設置 Cookie 名字,默認為 JSESSIONID; * sessionIdCookie.domain:設置 Cookie 的域名,默認空,即當前訪問的域名; * sessionIdCookie.path:設置 Cookie 的路徑,默認空,即存儲在域名根下; * sessionIdCookie.maxAge:設置 Cookie 的過期時間,秒為單位,默認 - 1 表示關閉瀏覽器時過期 Cookie; * sessionIdCookie.httpOnly:如果設置為 true,則客戶端不會暴露給客戶端腳本代碼,使用 HttpOnly cookie 有助于減少某些類型的跨站點腳本攻擊;此特性需要實現了 Servlet 2.5 MR6 及以上版本的規范的 Servlet 容器支持; * sessionManager.sessionIdCookieEnabled:是否啟用 / 禁用 Session Id Cookie,默認是啟用的;如果禁用后將不會設置 Session Id Cookie,即默認使用了 Servlet 容器的 JSESSIONID,且通過 URL 重寫(URL 中的 “;JSESSIONID=id” 部分)保存 Session Id。 另外我們可以如 “sessionManager. sessionIdCookie.name=sid” 這種方式操作 Cookie 模板。 ## 10.3 會話監聽器 會話監聽器用于監聽會話創建、過期及停止事件: ``` java public class MySessionListener1 implements SessionListener { @Override public void onStart(Session session) {//會話創建時觸發 System.out.println("會話創建:" + session.getId()); } @Override public void onExpiration(Session session) {//會話過期時觸發 System.out.println("會話過期:" + session.getId()); } @Override public void onStop(Session session) {//退出/會話過期時觸發 System.out.println("會話停止:" + session.getId()); } } ``` 如果只想監聽某一個事件,可以繼承 SessionListenerAdapter 實現: ``` java public class MySessionListener2 extends SessionListenerAdapter { @Override public void onStart(Session session) { System.out.println("會話創建:" + session.getId()); } } ``` 在 shiro-web.ini 配置文件中可以進行如下配置設置會話監聽器: ``` ini sessionListener1=com.github.zhangkaitao.shiro.chapter10.web.listener.MySessionListener1 sessionListener2=com.github.zhangkaitao.shiro.chapter10.web.listener.MySessionListener2 sessionManager.sessionListeners=$sessionListener1,$sessionListener2 ``` ## 10.4 會話存儲 / 持久化 Shiro 提供 SessionDAO 用于會話的 CRUD,即 DAO(Data Access Object)模式實現: ``` java //如DefaultSessionManager在創建完session后會調用該方法;如保存到關系數據庫/文件系統/NoSQL數據庫;即可以實現會話的持久化;返回會話ID;主要此處返回的ID.equals(session.getId()); Serializable create(Session session); //根據會話ID獲取會話 Session readSession(Serializable sessionId) throws UnknownSessionException; //更新會話;如更新會話最后訪問時間/停止會話/設置超時時間/設置移除屬性等會調用 void update(Session session) throws UnknownSessionException; //刪除會話;當會話過期/會話停止(如用戶退出時)會調用 void delete(Session session); //獲取當前所有活躍用戶,如果用戶量多此方法影響性能 Collection<Session> getActiveSessions(); ``` Shiro 內嵌了如下 SessionDAO 實現: ![](https://box.kancloud.cn/d596004e5f5bc0889d4fbb939f385e01_354x310.png) AbstractSessionDAO 提供了 SessionDAO 的基礎實現,如生成會話 ID 等;CachingSessionDAO 提供了對開發者透明的會話緩存的功能,只需要設置相應的 CacheManager 即可;MemorySessionDAO 直接在內存中進行會話維護;而 EnterpriseCacheSessionDAO 提供了緩存功能的會話維護,默認情況下使用 MapCache 實現,內部使用 ConcurrentHashMap 保存緩存的會話。 可以通過如下配置設置 SessionDAO: ``` java sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO sessionManager.sessionDAO=$sessionDAO ``` Shiro 提供了使用 Ehcache 進行會話存儲,Ehcache 可以配合 TerraCotta 實現容器無關的分布式集群。 首先在 pom.xml 里添加如下依賴: ``` xml <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.2</version> </dependency> ``` 接著配置 shiro-web.ini 文件: ``` ini sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO sessionDAO. activeSessionsCacheName=shiro-activeSessionCache sessionManager.sessionDAO=$sessionDAO cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager cacheManager.cacheManagerConfigFile=classpath:ehcache.xml securityManager.cacheManager = $cacheManager ``` * sessionDAO. activeSessionsCacheName:設置 Session 緩存名字,默認就是 shiro-activeSessionCache; * cacheManager:緩存管理器,用于管理緩存的,此處使用 Ehcache 實現; * cacheManager.cacheManagerConfigFile:設置 ehcache 緩存的配置文件; * securityManager.cacheManager:設置 SecurityManager 的 cacheManager,會自動設置實現了 CacheManagerAware 接口的相應對象,如 SessionDAO 的 cacheManager; 然后配置 ehcache.xml: ``` xml <cache name="shiro-activeSessionCache" maxEntriesLocalHeap="10000" overflowToDisk="false" eternal="false" diskPersistent="false" timeToLiveSeconds="0" timeToIdleSeconds="0" statistics="true"/> ``` Cache 的名字為 shiro-activeSessionCache,即設置的 sessionDAO 的 activeSessionsCacheName 屬性值。 另外可以通過如下 ini 配置設置會話 ID 生成器: ``` ini sessionIdGenerator=org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator sessionDAO.sessionIdGenerator=$sessionIdGenerator ``` 用于生成會話 ID,默認就是 JavaUuidSessionIdGenerator,使用 java.util.UUID 生成。 如果自定義實現 SessionDAO,繼承 CachingSessionDAO 即可: ``` java public class MySessionDAO extends CachingSessionDAO { private JdbcTemplate jdbcTemplate = JdbcTemplateUtils.jdbcTemplate(); protected Serializable doCreate(Session session) { Serializable sessionId = generateSessionId(session); assignSessionId(session, sessionId); String sql = "insert into sessions(id, session) values(?,?)"; jdbcTemplate.update(sql, sessionId, SerializableUtils.serialize(session)); return session.getId(); } protected void doUpdate(Session session) { if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid()) { return; //如果會話過期/停止 沒必要再更新了 } String sql = "update sessions set session=? where id=?"; jdbcTemplate.update(sql, SerializableUtils.serialize(session), session.getId()); } protected void doDelete(Session session) { String sql = "delete from sessions where id=?"; jdbcTemplate.update(sql, session.getId()); } protected Session doReadSession(Serializable sessionId) { String sql = "select session from sessions where id=?"; List<String> sessionStrList = jdbcTemplate.queryForList(sql, String.class, sessionId); if(sessionStrList.size() == 0) return null; return SerializableUtils.deserialize(sessionStrList.get(0)); } } ``` doCreate/doUpdate/doDelete/doReadSession 分別代表創建 / 修改 / 刪除 / 讀取會話;此處通過把會話序列化后存儲到數據庫實現;接著在 shiro-web.ini 中配置: `sessionDAO=com.github.zhangkaitao.shiro.chapter10.session.dao.MySessionDAO` 其他設置和之前一樣,因為繼承了 CachingSessionDAO;所有在讀取時會先查緩存中是否存在,如果找不到才到數據庫中查找。 ## 10.5 會話驗證 Shiro 提供了會話驗證調度器,用于定期的驗證會話是否已過期,如果過期將停止會話;出于性能考慮,一般情況下都是獲取會話時來驗證會話是否過期并停止會話的;但是如在 web 環境中,如果用戶不主動退出是不知道會話是否過期的,因此需要定期的檢測會話是否過期,Shiro 提供了會話驗證調度器 SessionValidationScheduler 來做這件事情。 可以通過如下 ini 配置開啟會話驗證: ``` ini sessionValidationScheduler=org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler sessionValidationScheduler.interval = 3600000 sessionValidationScheduler.sessionManager=$sessionManager sessionManager.globalSessionTimeout=1800000 sessionManager.sessionValidationSchedulerEnabled=true sessionManager.sessionValidationScheduler=$sessionValidationScheduler ``` * sessionValidationScheduler:會話驗證調度器,sessionManager 默認就是使用 ExecutorServiceSessionValidationScheduler,其使用 JDK 的 ScheduledExecutorService 進行定期調度并驗證會話是否過期; * sessionValidationScheduler.interval:設置調度時間間隔,單位毫秒,默認就是 1 小時; * sessionValidationScheduler.sessionManager:設置會話驗證調度器進行會話驗證時的會話管理器; * sessionManager.globalSessionTimeout:設置全局會話超時時間,默認 30 分鐘,即如果 30 分鐘內沒有訪問會話將過期; * sessionManager.sessionValidationSchedulerEnabled:是否開啟會話驗證器,默認是開啟的; * sessionManager.sessionValidationScheduler:設置會話驗證調度器,默認就是使用 ExecutorServiceSessionValidationScheduler。 Shiro 也提供了使用 Quartz 會話驗證調度器: ``` ini sessionValidationScheduler=org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler sessionValidationScheduler.sessionValidationInterval = 3600000 sessionValidationScheduler.sessionManager=$sessionManager ``` 使用時需要導入 shiro-quartz 依賴: ``` xml <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>1.2.2</version> </dependency> ``` 如上會話驗證調度器實現都是直接調用 AbstractValidatingSessionManager 的 validateSessions 方法進行驗證,其直接調用 SessionDAO 的 getActiveSessions 方法獲取所有會話進行驗證,如果會話比較多,會影響性能;可以考慮如分頁獲取會話并進行驗證,如 com.github.zhangkaitao.shiro.chapter10.session.scheduler.MySessionValidationScheduler: ``` java //分頁獲取會話并驗證 String sql = "select session from sessions limit ?,?"; int start = 0; //起始記錄 int size = 20; //每頁大小 List<String> sessionList = jdbcTemplate.queryForList(sql, String.class, start, size); while(sessionList.size() > 0) { for(String sessionStr : sessionList) { try { Session session = SerializableUtils.deserialize(sessionStr); Method validateMethod = ReflectionUtils.findMethod(AbstractValidatingSessionManager.class, "validate", Session.class, SessionKey.class); validateMethod.setAccessible(true); ReflectionUtils.invokeMethod(validateMethod, sessionManager, session, new DefaultSessionKey(session.getId())); } catch (Exception e) { //ignore } } start = start + size; sessionList = jdbcTemplate.queryForList(sql, String.class, start, size); } ``` 其直接改造自 ExecutorServiceSessionValidationScheduler,如上代碼是驗證的核心代碼,可以根據自己的需求改造此驗證調度器器;ini 的配置和之前的類似。 如果在會話過期時不想刪除過期的會話,可以通過如下 ini 配置進行設置: `sessionManager.deleteInvalidSessions=false` 默認是開啟的,在會話過期后會調用 SessionDAO 的 delete 方法刪除會話:如會話時持久化存儲的,可以調用此方法進行刪除。 如果是在獲取會話時驗證了會話已過期,將拋出 InvalidSessionException;因此需要捕獲這個異常并跳轉到相應的頁面告訴用戶會話已過期,讓其重新登錄,如可以在 web.xml 配置相應的錯誤頁面: ``` xml <error-page> <exception-type>org.apache.shiro.session.InvalidSessionException</exception-type> <location>/invalidSession.jsp</location> </error-page> ``` ## 10.6 sessionFactory sessionFactory 是創建會話的工廠,根據相應的 Subject 上下文信息來創建會話;默認提供了 SimpleSessionFactory 用來創建 SimpleSession 會話。 首先自定義一個 Session: ``` java public class OnlineSession extends SimpleSession { public static enum OnlineStatus { on_line("在線"), hidden("隱身"), force_logout("強制退出"); private final String info; private OnlineStatus(String info) { this.info = info; } public String getInfo() { return info; } } private String userAgent; //用戶瀏覽器類型 private OnlineStatus status = OnlineStatus.on_line; //在線狀態 private String systemHost; //用戶登錄時系統IP //省略其他 } ``` OnlineSession 用于保存當前登錄用戶的在線狀態,支持如離線等狀態的控制。 接著自定義 SessionFactory: ``` java public class OnlineSessionFactory implements SessionFactory { @Override public Session createSession(SessionContext initData) { OnlineSession session = new OnlineSession(); if (initData != null && initData instanceof WebSessionContext) { WebSessionContext sessionContext = (WebSessionContext) initData; HttpServletRequest request = (HttpServletRequest) sessionContext.getServletRequest(); if (request != null) { session.setHost(IpUtils.getIpAddr(request)); session.setUserAgent(request.getHeader("User-Agent")); session.setSystemHost(request.getLocalAddr() + ":" + request.getLocalPort()); } } return session; } } ``` 根據會話上下文創建相應的 OnlineSession。 最后在 shiro-web.ini 配置文件中配置: ``` ini sessionFactory=org.apache.shiro.session.mgt.OnlineSessionFactory sessionManager.sessionFactory=$sessionFactory ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看