新聞中心
Shiro提供了完整的企業(yè)級會話管理功能,不依賴于底層容器(如web容器tomcat),不管JavaSE還是JavaEE環(huán)境都可以使用,提供了會話管理、會話事件監(jiān)聽、會話存儲/持久化、容器無關(guān)的集群、失效/過期支持、對Web的透明支持、SSO單點登錄的支持等特性。即直接使用Shiro的會話管理可以直接替換如Web容器的會話管理。
從策劃到設(shè)計制作,每一步都追求做到細(xì)膩,制作可持續(xù)發(fā)展的企業(yè)網(wǎng)站。為客戶提供成都網(wǎng)站設(shè)計、網(wǎng)站制作、網(wǎng)站策劃、網(wǎng)頁設(shè)計、域名申請、網(wǎng)站空間、網(wǎng)絡(luò)營銷、VI設(shè)計、 網(wǎng)站改版、漏洞修補(bǔ)等服務(wù)。為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,以客戶的口碑塑造優(yōu)易品牌,攜手廣大客戶,共同發(fā)展進(jìn)步。
會話
所謂會話,即用戶訪問應(yīng)用時保持的連接關(guān)系,在多次交互中應(yīng)用能夠識別出當(dāng)前訪問的用戶是誰,且可以在多次交互中保存一些數(shù)據(jù)。如訪問一些網(wǎng)站時登錄成功后,網(wǎng)站可以記住用戶,且在退出之前都可以識別當(dāng)前用戶是誰。
Shiro的會話支持不僅可以在普通的JavaSE應(yīng)用中使用,也可以在JavaEE應(yīng)用中使用,如web應(yīng)用。且使用方式是一致的。
Java代碼
login("classpath:shiro.ini", "zhang", "123"); Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession();
登錄成功后使用Subject.getSession()
即可獲取會話;其等價于Subject.getSession(true)
,即如果當(dāng)前沒有創(chuàng)建Session對象會創(chuàng)建一個;另外Subject.getSession(false)
,如果當(dāng)前沒有創(chuàng)建Session則返回null(不過默認(rèn)情況下如果啟用會話存儲功能的話在創(chuàng)建Subject時會主動創(chuàng)建一個Session)。
Java代碼
session.getId();
獲取當(dāng)前會話的唯一標(biāo)識。
Java代碼
session.getHost();
獲取當(dāng)前Subject的主機(jī)地址,該地址是通過HostAuthenticationToken.getHost()提供的。
Java代碼
session.getTimeout(); session.setTimeout(毫秒);
獲取/設(shè)置當(dāng)前Session的過期時間;如果不設(shè)置默認(rèn)是會話管理器的全局過期時間。
Java代碼
session.getStartTimestamp(); session.getLastAccessTime();
獲取會話的啟動時間及最后訪問時間;如果是JavaSE應(yīng)用需要自己定期調(diào)用session.touch()去更新最后訪問時間;如果是Web應(yīng)用,每次進(jìn)入ShiroFilter都會自動調(diào)用session.touch()來更新最后訪問時間。
Java代碼
session.touch(); session.stop();
更新會話最后訪問時間及銷毀會話;當(dāng)Subject.logout()時會自動調(diào)用stop方法來銷毀會話。如果在web中,調(diào)用javax.servlet.http.HttpSession. invalidate()也會自動調(diào)用Shiro Session.stop方法進(jìn)行銷毀Shiro的會話。
Java代碼
session.setAttribute("key", "123"); Assert.assertEquals("123", session.getAttribute("key")); session.removeAttribute("key");
設(shè)置/獲取/刪除會話屬性;在整個會話范圍內(nèi)都可以對這些屬性進(jìn)行操作。
Shiro提供的會話可以用于JavaSE/JavaEE環(huán)境,不依賴于任何底層容器,可以獨(dú)立使用,是完整的會話模塊。
會話管理器
會話管理器管理著應(yīng)用中所有Subject的會話的創(chuàng)建、維護(hù)、刪除、失效、驗證等工作。是Shiro的核心組件,頂層組件SecurityManager直接繼承了SessionManager,且提供了SessionsSecurityManager實現(xiàn)直接把會話管理委托給相應(yīng)的SessionManager,DefaultSecurityManager及
DefaultWebSecurityManager默認(rèn)SecurityManager都繼承了SessionsSecurityManager。
SecurityManager提供了如下接口:
Java代碼
Session start(SessionContext context); //啟動會話 Session getSession(SessionKey key) throws SessionException; //根據(jù)會話Key獲取會話
另外用于Web環(huán)境的WebSessionManager又提供了如下接口:
Java代碼
boolean isServletContainerSessions();//是否使用Servlet容器的會話
Shiro還提供了ValidatingSessionManager用于驗資并過期會話:
Java代碼
void validateSessions();//驗證所有會話是否過期
Shiro提供了三個默認(rèn)實現(xiàn):
DefaultSessionManager:DefaultSecurityManager使用的默認(rèn)實現(xiàn),用于JavaSE環(huán)境;
ServletContainerSessionManager:DefaultWebSecurityManager使用的默認(rèn)實現(xiàn),用于Web環(huán)境,其直接使用Servlet容器的會話;
DefaultWebSessionManager:用于Web環(huán)境的實現(xiàn),可以替代ServletContainerSessionManager,自己維護(hù)著會話,直接廢棄了Servlet容器的會話管理。
替換SecurityManager默認(rèn)的SessionManager可以在ini中配置(shiro.ini):
Java代碼
[main] sessionManager=org.apache.shiro.session.mgt.DefaultSessionManager securityManager.sessionManager=$sessionManager
Web環(huán)境下的ini配置(shiro-web.ini):
Java代碼
[main] sessionManager=org.apache.shiro.web.session.mgt.ServletContainerSessionManager securityManager.sessionManager=$sessionManager
另外可以設(shè)置會話的全局過期時間(毫秒為單位),默認(rèn)30分鐘:
Java代碼
sessionManager. globalSessionTimeout=1800000
默認(rèn)情況下globalSessionTimeout將應(yīng)用給所有Session。可以單獨(dú)設(shè)置每個Session的timeout屬性來為每個Session設(shè)置其超時時間。
另外如果使用ServletContainerSessionManager進(jìn)行會話管理,Session的超時依賴于底層Servlet容器的超時時間,可以在web.xml中配置其會話的超時時間(分鐘為單位):
Java代碼
30
在Servlet容器中,默認(rèn)使用JSESSIONID Cookie維護(hù)會話,且會話默認(rèn)是跟容器綁定的;在某些情況下可能需要使用自己的會話機(jī)制,此時我們可以使用DefaultWebSessionManager來維護(hù)會話:
Java代碼
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創(chuàng)建會話Cookie的模板:
sessionIdCookie.name:設(shè)置Cookie名字,默認(rèn)為JSESSIONID;
sessionIdCookie.domain:設(shè)置Cookie的域名,默認(rèn)空,即當(dāng)前訪問的域名;
sessionIdCookie.path:設(shè)置Cookie的路徑,默認(rèn)空,即存儲在域名根下;
sessionIdCookie.maxAge:設(shè)置Cookie的過期時間,秒為單位,默認(rèn)-1表示關(guān)閉瀏覽器時過期Cookie;
sessionIdCookie.httpOnly:如果設(shè)置為true,則客戶端不會暴露給客戶端腳本代碼,使用HttpOnly cookie有助于減少某些類型的跨站點腳本攻擊;此特性需要實現(xiàn)了Servlet 2.5 MR6及以上版本的規(guī)范的Servlet容器支持;
sessionManager.sessionIdCookieEnabled:是否啟用/禁用Session Id Cookie,默認(rèn)是啟用的;如果禁用后將不會設(shè)置Session Id Cookie,即默認(rèn)使用了Servlet容器的JSESSIONID,且通過URL重寫(URL中的“;JSESSIONID=id”部分)保存Session Id。
另外我們可以如“sessionManager. sessionIdCookie.name=sid”這種方式操作Cookie模板。
會話監(jiān)聽器
會話監(jiān)聽器用于監(jiān)聽會話創(chuàng)建、過期及停止事件:
Java代碼
public class MySessionListener1 implements SessionListener { @Override public void onStart(Session session) {//會話創(chuàng)建時觸發(fā) System.out.println("會話創(chuàng)建:" + session.getId()); } @Override public void onExpiration(Session session) {//會話過期時觸發(fā) System.out.println("會話過期:" + session.getId()); } @Override public void onStop(Session session) {//退出/會話過期時觸發(fā) System.out.println("會話停止:" + session.getId()); } }
如果只想監(jiān)聽某一個事件,可以繼承SessionListenerAdapter實現(xiàn):
Java代碼
public class MySessionListener2 extends SessionListenerAdapter { @Override public void onStart(Session session) { System.out.println("會話創(chuàng)建:" + session.getId()); } }
在shiro-web.ini配置文件中可以進(jìn)行如下配置設(shè)置會話監(jiān)聽器:
Java代碼
sessionListener1=com.github.zhangkaitao.shiro.chapter10.web.listener.MySessionListener1 sessionListener2=com.github.zhangkaitao.shiro.chapter10.web.listener.MySessionListener2 sessionManager.sessionListeners=$sessionListener1,$sessionListener2
會話存儲/持久化
Shiro提供SessionDAO用于會話的CRUD,即DAO(Data Access Object)模式實現(xiàn):
Java代碼
//如DefaultSessionManager在創(chuàng)建完session后會調(diào)用該方法;如保存到關(guān)系數(shù)據(jù)庫/文件系統(tǒng)/NoSql數(shù)據(jù)庫;即可以實現(xiàn)會話的持久化;返回會話ID;主要此處返回的ID.equals(session.getId()); Serializable create(Session session); //根據(jù)會話ID獲取會話 Session readSession(Serializable sessionId) throws UnknownSessionException; //更新會話;如更新會話最后訪問時間/停止會話/設(shè)置超時時間/設(shè)置移除屬性等會調(diào)用 void update(Session session) throws UnknownSessionException; //刪除會話;當(dāng)會話過期/會話停止(如用戶退出時)會調(diào)用 void delete(Session session); //獲取當(dāng)前所有活躍用戶,如果用戶量多此方法影響性能 CollectiongetActiveSessions();
Shiro內(nèi)嵌了如下SessionDAO實現(xiàn):
AbstractSessionDAO提供了SessionDAO的基礎(chǔ)實現(xiàn),如生成會話ID等;CachingSessionDAO提供了對開發(fā)者透明的會話緩存的功能,只需要設(shè)置相應(yīng)的CacheManager即可;MemorySessionDAO直接在內(nèi)存中進(jìn)行會話維護(hù);而EnterpriseCacheSessionDAO提供了緩存功能的會話維護(hù),默認(rèn)情況下使用MapCache實現(xiàn),內(nèi)部使用ConcurrentHashMap保存緩存的會話。
可以通過如下配置設(shè)置SessionDAO:
Java代碼
sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO sessionManager.sessionDAO=$sessionDAO
Shiro提供了使用Ehcache進(jìn)行會話存儲,Ehcache可以配合TerraCotta實現(xiàn)容器無關(guān)的分布式集群。
首先在pom.xml里添加如下依賴:
Java代碼
org.apache.shiro shiro-ehcache 1.2.2
接著配置shiro-web.ini文件:
Java代碼
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:設(shè)置Session緩存名字,默認(rèn)就是shiro-activeSessionCache;
cacheManager:緩存管理器,用于管理緩存的,此處使用Ehcache實現(xiàn);
cacheManager.cacheManagerConfigFile:設(shè)置ehcache緩存的配置文件;
securityManager.cacheManager:設(shè)置SecurityManager的cacheManager,會自動設(shè)置實現(xiàn)了CacheManagerAware接口的相應(yīng)對象,如SessionDAO的cacheManager;
然后配置ehcache.xml:
Java代碼
Cache的名字為shiro-activeSessionCache,即設(shè)置的sessionDAO的activeSessionsCacheName屬性值。
另外可以通過如下ini配置設(shè)置會話ID生成器:
Java代碼
sessionIdGenerator=org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator sessionDAO.sessionIdGenerator=$sessionIdGenerator
用于生成會話ID,默認(rèn)就是JavaUuidSessionIdGenerator,使用java.util.UUID生成。
如果自定義實現(xiàn)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=?"; ListsessionStrList = jdbcTemplate.queryForList(sql, String.class, sessionId); if(sessionStrList.size() == 0) return null; return SerializableUtils.deserialize(sessionStrList.get(0)); } }
doCreate/doUpdate/doDelete/doReadSession分別代表創(chuàng)建/修改/刪除/讀取會話;此處通過把會話序列化后存儲到數(shù)據(jù)庫實現(xiàn);接著在shiro-web.ini中配置:
Java代碼
sessionDAO=com.github.zhangkaitao.shiro.chapter10.session.dao.MySessionDAO
其他設(shè)置和之前一樣,因為繼承了CachingSessionDAO;所有在讀取時會先查緩存中是否存在,如果找不到才到數(shù)據(jù)庫中查找。
會話驗證
Shiro提供了會話驗證調(diào)度器,用于定期的驗證會話是否已過期,如果過期將停止會話;出于性能考慮,一般情況下都是獲取會話時來驗證會話是否過期并停止會話的;但是如在web環(huán)境中,如果用戶不主動退出是不知道會話是否過期的,因此需要定期的檢測會話是否過期,Shiro提供了會話驗證調(diào)度器SessionValidationScheduler來做這件事情。
可以通過如下ini配置開啟會話驗證:
Java代碼
sessionValidationScheduler=org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler sessionValidationScheduler.interval = 3600000 sessionValidationScheduler.sessionManager=$sessionManager sessionManager.globalSessionTimeout=1800000 sessionManager.sessionValidationSchedulerEnabled=true sessionManager.sessionValidationScheduler=$sessionValidationScheduler
sessionValidationScheduler:會話驗證調(diào)度器,sessionManager默認(rèn)就是使用ExecutorServiceSessionValidationScheduler,其使用JDK的ScheduledExecutorService進(jìn)行定期調(diào)度并驗證會話是否過期;
sessionValidationScheduler.interval:設(shè)置調(diào)度時間間隔,單位毫秒,默認(rèn)就是1小時;
sessionValidationScheduler.sessionManager:設(shè)置會話驗證調(diào)度器進(jìn)行會話驗證時的會話管理器;
sessionManager.globalSessionTimeout:設(shè)置全局會話超時時間,默認(rèn)30分鐘,即如果30分鐘內(nèi)沒有訪問會話將過期;
sessionManager.sessionValidationSchedulerEnabled:是否開啟會話驗證器,默認(rèn)是開啟的;
sessionManager.sessionValidationScheduler:設(shè)置會話驗證調(diào)度器,默認(rèn)就是使用ExecutorServiceSessionValidationScheduler。
Shiro也提供了使用Quartz會話驗證調(diào)度器:
Java代碼
sessionValidationScheduler=org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler sessionValidationScheduler.sessionValidationInterval = 3600000 sessionValidationScheduler.sessionManager=$sessionManager
使用時需要導(dǎo)入shiro-quartz依賴:
Java代碼
org.apache.shiro shiro-quartz 1.2.2
如上會話驗證調(diào)度器實現(xiàn)都是直接調(diào)用AbstractValidatingSessionManager 的validateSessions方法進(jìn)行驗證,其直接調(diào)用SessionDAO的getActiveSessions方法獲取所有會話進(jìn)行驗證,如果會話比較多,會影響性能;可以考慮如分頁獲取會話并進(jìn)行驗證,如com.github.zhangkaitao.shiro.chapter10.session.scheduler.MySessionValidationScheduler:
Java代碼
//分頁獲取會話并驗證 String sql = "select session from sessions limit ?,?"; int start = 0; //起始記錄 int size = 20; //每頁大小 ListsessionList = 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,如上代碼是驗證的核心代碼,可以根據(jù)自己的需求改造此驗證調(diào)度器器;ini的配置和之前的類似。
如果在會話過期時不想刪除過期的會話,可以通過如下ini配置進(jìn)行設(shè)置:
Java代碼
sessionManager.deleteInvalidSessions=false
默認(rèn)是開啟的,在會話過期后會調(diào)用SessionDAO的delete方法刪除會話:如會話時持久化存儲的,可以調(diào)用此方法進(jìn)行刪除。
如果是在獲取會話時驗證了會話已過期,將拋出InvalidSessionException;因此需要捕獲這個異常并跳轉(zhuǎn)到相應(yīng)的頁面告訴用戶會話已過期,讓其重新登錄,如可以在web.xml配置相應(yīng)的錯誤頁面:
Java代碼
org.apache.shiro.session.InvalidSessionException /invalidSession.jsp
sessionFactory
sessionFactory是創(chuàng)建會話的工廠,根據(jù)相應(yīng)的Subject上下文信息來創(chuàng)建會話;默認(rèn)提供了SimpleSessionFactory用來創(chuàng)建SimpleSession會話。
首先自定義一個Session:
Java代碼
public class OnlineSession extends SimpleSession { public static enum OnlineStatus { on_line("在線"), hidden("隱身"), force_logout("強(qiáng)制退出"); 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; //在線狀態(tài) private String systemHost; //用戶登錄時系統(tǒng)IP //省略其他 }
OnlineSession用于保存當(dāng)前登錄用戶的在線狀態(tài),支持如離線等狀態(tài)的控制。
接著自定義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; } }
根據(jù)會話上下文創(chuàng)建相應(yīng)的OnlineSession。
最后在shiro-web.ini配置文件中配置:
Java代碼
sessionFactory=org.apache.shiro.session.mgt.OnlineSessionFactory sessionManager.sessionFactory=$sessionFactory
總結(jié)
以上所述是小編給大家介紹的shiro會話管理,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對創(chuàng)新互聯(lián)網(wǎng)站的支持!
新聞名稱:shiro會話管理示例代碼
文章出自:http://fisionsoft.com.cn/article/pdgioo.html