Shiro会话管理

转自:http://www.zblog.us/java/shiro_session_manager.html

shiro提供了一个完整的企业级会话管理解决方案,不再依赖web容器。可以在web和非web环境下使用。

shiro的session特性

  • 基于POJO/J2SE:shiro中session相关的类都是基于接口实现的简单的java对象(POJO),兼容所有java对象的配置方式,扩展也更方便,完全可以定制自己的会话管理功能 。
  • 简单灵活的会话存储/持久化:因为shiro中的session对象是基于简单的java对象的,所以你可以将session存储在任何地方,例如,文件,各种数据库,内存中等。
  • 容器无关的集群功能:shiro中的session可以很容易的集成第三方的缓存产品完成集群的功能。例如,Ehcache + Terracotta, Coherence, GigaSpaces等。你可以很容易的实现会话集群而无需关注底层的容器实现。
  • 异构客户端的访问:可以实现web中的session和非web项目中的session共享。
  • 会话事件监听:提供对对session整个生命周期的监听。
  • 保存主机地址:在会话开始session会存用户的ip地址和主机名,以此可以判断用户的位置。
  • 会话失效/过期的支持:用户长时间处于不活跃状态可以使会话过期,调用touch()方法,可以主动更新最后访问时间,让会话处于活跃状态。
  • 透明的Web支持:shiro全面支持Servlet 2.5中的session规范。这意味着你可以将你现有的web程序改为shiro会话,而无需修改代码。
  • 单点登录的支持:shiro session基于普通java对象,使得它更容易存储和共享,可以实现跨应用程序共享。可以根据共享的会话,来保证认证状态到另一个程序。从而实现单点登录。

使用会话

可以从当前的Subject中获取会话。
   
   
  1. Subject currentUser = SecurityUtils.getSubject();
  2. Session session = currentUser.getSession();
  3. session.setAttribute( "someKey", someValue);
 获取session的subject.getSession()方法等价于currentUser.getSubject(true)。
  • Suject.getSession(boolean create) 与web中的 HttpServletRequest.getSession(boolean create) 类似。
  • 如果Subject已经拥有一个session,则方法中的boolean类型参数将会忽略,并直接返回已经存在的session。
  • 如果Subject里没有拥有session,如果参数为true,则创建一个新的session并返回。
  • 如果Subject里没有拥有session,如果参数为false,则不会创建新的session,并返回null。
返回值 方法名 描述
Object getAttribute(Object key)  根据key标识返回绑定到session的对象
Collection<Object> getAttributeKeys()  获取在session中存储的所有的key
String getHost() 获取当前主机ip地址,如果未知,返回null
Serializable getId()  获取session的唯一id
Date getLastAccessTime()  获取最后的访问时间
Date getStartTimestamp()  获取session的启动时间
long getTimeout()  获取session失效时间,单位毫秒
void setTimeout(long maxIdleTimeInMillis)  设置session的失效时间
Object removeAttribute(Object key)  通过key移除session中绑定的对象
void setAttribute(Object key, Object value)  设置session会话属性
void stop()  销毁会话
void touch()  更新会话最后访问时间

会话管理器

SessionManager管理所有Subject的session包括创建、维护、删除、失效、验证等工作。SessionManager是顶层组件,由SecurityManager管理。
SecurityManager的实现类DefaultSecurityManager及DefaultWebSecurityManager继承了SessionsSecurityManager。
SessionsSecurityManager可以把相应的会话管理委托给SessionManager。
例如SessionsSecurityManager中的代码
   
   
  1. public Session start(SessionContext context) throws AuthorizationException {
  2. //委托给SessionManager
  3. return this.sessionManager.start(context);
  4. }
  5. public Session getSession(SessionKey key) throws SessionException {
  6. //委托给SessionManager
  7. return this.sessionManager.getSession(key);
  8. }
SecurityManager相关的类。
非web相关的

shiro session

web相关的

web session

shiro提供了三个SessionManager的实现
  • DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于非web环境。
  • ServletContainerSessionManager:DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话。
  • DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager自己维护着会话,容器无关。
像SecurityManager其它组件一样,可以使用getter/setter方法获取和设置组件,同时也支持ini配置。
    
    
  1. [main]
  2. ...
  3. sessionManager = com.foo.my.SessionManagerImplementation
  4. securityManager.sessionManager = $sessionManager

会话失效时间

全局会话失效时间
    
    
  1. [main]
  2. ...
  3. # 3,600,000 milliseconds = 1 hour
  4. securityManager.sessionManager.globalSessionTimeout = 3600000
也可以为每个session单独设置会话失效时间
调用session的 setTimeout(long maxIdleTimeInMillis) ,参数为毫秒

对于 ServletContainerSessionManager,由于依赖于具体容器,所有失效时间要在容器里设置。

DefaultWebSessionManager容器无关的SessionMannager

ini配置

   
   
  1. sessionIdCookie=org.apache.shiro.web.servlet.SimpleCookie
  2. sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
  3. sessionIdCookie.name=sid
  4. #sessionIdCookie.domain=zblog.us
  5. #sessionIdCookie.path=
  6. sessionIdCookie.maxAge=1800
  7. sessionIdCookie.httpOnly=true
  8. sessionManager.sessionIdCookie=$sessionIdCookie
  9. sessionManager.sessionIdCookieEnabled=true
  10. securityManager.sessionManager=$sessionManager
sessionIdCookie是sessionManager创建会话Cookie的模板。
  • sessionIdCookie.name:设置Cookie名字,默认为JSESSIONID;
  • sessionIdCookie.domain:设置Cookie的域名,默认空,即当前访问的域名;
  • sessionIdCookie.path:设置Cookie的路径,默认空,即存储在域名根下;
  • sessionIdCookie.maxAge:设置Cookie的过期时间,秒为单位,默认-1表示关闭浏览器时过期Cookie;
  • sessionIdCookie.httpOnly:如果设置为true,更安全, 防止 XSS 攻击
  • sessionManager.sessionIdCookieEnabled:是否启用/禁用Session Id Cookie,默认是启用的;如果禁用后将不会设置Session Id Cookie,即默认使用了Servlet容器的JSESSIONID,且通过URL重写(URL中的“;JSESSIONID=id”部分)保存Session Id。

会话监听

ini配置
    
    
  1. [main]
  2. ...
  3. aSessionListener = com.foo.my.SessionListener
  4. anotherSessionListener = com.foo.my.OtherSessionListener
  5. securityManager.sessionManager.sessionListeners = $aSessionListener, $anotherSessionListener
要写自己的监听器,需要实现 SessionListener  接口
    
    
  1. public class MySessionListener implements SessionListener {
  2. @Override
  3. public void onStart(Session session) {
  4. //会话创建时触发
  5. System.out.println("会话创建:" + session.getId());
  6. }
  7. @Override
  8. public void onExpiration(Session session) {
  9. //会话过期时触发
  10. System.out.println("会话过期:" + session.getId());
  11. }
  12. @Override
  13. public void onStop(Session session) {
  14. //退出/会话过期时触发
  15. System.out.println("会话停止:" + session.getId());
  16. }
  17. }
注:session监听是对所有的session监听,而不是针对某个特殊的session

会话存储/持久化

Session可以存储在内存或者各种数据库中。 SessionManager 委托SeesionDao对session进行增删改查。
你可以为SessionManager配置SessionDao
    
    
  1. [main]
  2. ...
  3. sessionDAO = com.foo.my.SessionDAO
  4. securityManager.sessionManager.sessionDAO = $sessionDAO
如果使用的是 ServletContainerSessionManager,由于它是容器相关的,session也是有对应的容器管理的,无法使用SessionDao。
对于web项目可以使用 DefaultWebSessionManager。
    
    
  1. [main]
  2. ...
  3. sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
  4. securityManager.sessionManager = $sessionManagers
  5. # Configure a SessionDAO and then set it:
  6. securityManager.sessionManager.sessionDAO = $sessionDAO
EHCache SessionDAO
EHCache SessionDAO存储Session到内存,如果内存不够用的话,会保存到磁盘。Ehcache配合TerraCotta可以实现容器无关的分布式集群。
启用ehcache支持,在pom.xml中添加
    
    
  1. <dependency>
  2. <groupId>org.apache.shiro</groupId>
  3. <artifactId>shiro-ehcache</artifactId>
  4. <version>1.2.3</version>
  5. </dependency>
为shiro所有用到缓存的地方,都配置成ehcache,自然SessionDao,也会被配置为EHCache来保存session。
ini配置
    
    
  1. sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
  2. #shiro默认的activeSessionsCacheName为shiro-activeSessionCache,
  3. #如需重命名,可以这样设置
  4. #sessionDAO.activeSessionsCacheName=shiro-activeSessionCache
  5. sessionManager.sessionDAO=$sessionDAO
  6. cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
  7. #shiro提供了默认的配置文件,如需自定义可以这样设置
  8. #cacheManager.cacheManagerConfigFile=classpath:ehcache.xml
  9. securityManager.cacheManager = $cacheManager
说明:
  • sessionDAO.activeSessionsCacheName:设置Session缓存名字,默认就是shiro-activeSessionCache。
  • cacheManager:缓存管理器,用于管理缓存的,这里使用Ehcache实现。
  • cacheManager.cacheManagerConfigFile:设置ehcache缓存的配置文件,默认文件位置在shiro-ehcache包中。
  • securityManager.cacheManager:设置SecurityManager的cacheManager,会自动设置实现了CacheManagerAware接口的相应对象,如SessionDAO的cacheManager。
EHCache的配置,默认在shiro-ehcache包中
    
    
  1. <cache name="shiro-activeSessionCache"
  2. maxElementsInMemory="10000"
  3. overflowToDisk="true"
  4. eternal="true"
  5. timeToLiveSeconds="0"
  6. timeToIdleSeconds="0"
  7. diskPersistent="true"
  8. diskExpiryThreadIntervalSeconds="600"/>
其中,name属性必须与上边ini配置中activeSessionsCacheNamename一致。
如果要自己配置的话有两点很重要。
  • overflowToDisk=”true” :设置为true,表示如果内存不够用了,会将会话保存到硬盘。
  • eternal=”true” :设置为true,表示会话对象的缓存不会被自动设置为过期或删除。shiro的session检查机制是基于调度器定时检测的,如果自动删除或者设置过期的话,shiro是无法知道session是否过期的,这样就会出现问题,所以要设置为true。

Session ID生成器

在每次创建session时,SessionDAO都会使用 SessionIdGenerator生成一个新的session ID, SessionIdGenerator默认实现是JavaUuidSessionIdGenerator,也就是生成UUID。
可以自定义自己的 SessionIdGenerator。
ini配置如下

    
    
  1. [main]
  2. ...
  3. sessionIdGenerator = com.my.session.SessionIdGenerator
  4. securityManager.sessionManager.sessionDAO.sessionIdGenerator = $sessionIdGenerator
SessionDao的相关类

session dao

SessionDao接口定义了以下方法。
    
    
  1. //如DefaultSessionManager在创建完session后会调用该方法;
  2. //如保存到关系数据库/文件系统/NoSQL数据库;即可以实现会话的持久化;
  3. //返回会话ID;主要此处返回的ID.equals(session.getId());
  4. Serializable create(Session session);
  5. //根据会话ID获取会话
  6. Session readSession(Serializable sessionId) throws UnknownSessionException;
  7. //更新会话;如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
  8. void update(Session session) throws UnknownSessionException;
  9. //删除会话;当会话过期/会话停止(如用户退出时)会调用
  10. void delete(Session session);
  11. //获取当前所有活跃用户
  12. Collection<Session> getActiveSessions();
  • AbstractSessionDAO提供了SessionDAO的一些实现,例如生成会话id,创建会话。
  • CachingSessionDAO提供了会话缓存的管理功能,需要为其设置CacheManager。
  • MemorySessionDAO基于内存的,会话持久化实现。
  • EnterpriseCacheSessionDAO继承MemorySessionDAO,并为其提供了一个MapCache作为简单的缓存管理器。在生产环境中如果直接使用EnterpriseCacheSessionDAO,推荐为其设置CacheManager,例如基于EHCache的EhCacheManager ,因为MapCache容易出现内存溢出,因为它无法持久化数据到硬盘。

自定义SessionDao继承 CachingSessionDAO即可,例如实现把会话保存到数据库,同时要为SessionDao设置 CacheManager,这样在获取session的时候会先从缓存获取,获取不到的时候才会查询数据库。

会话验证

Session必须通过验证才可以将无效过过期的session删除,出于性能的考虑,只有在获取会话的时候去验证会话是否过期。如果用户不主动退出,是无法知道session是否失效或过期的。如果不定期清理,session会越来越多。因此需要定期清理,shiro提供了会话验证调度器 SessionValidationScheduler来定期完成清除session的工作。
默认的调度器
默认的 SessionValidationScheduler调度器实现是 ExecutorServiceSessionValidationScheduler (基于JDKScheduledExecutorService实现的)。默认的调度周期是1小时,也就是没小时都会执行一次session验证,并清除过期或无效的session。
ini配置
    
    
  1. [main]
  2. ...
  3. sessionValidationScheduler = org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler
  4. # 默认是3,600,000 毫秒 = 1 小时:
  5. sessionValidationScheduler.interval = 3600000
  6. securityManager.sessionManager.sessionValidationScheduler = $sessionValidationScheduler
关闭调度器(默认是开启)
    
    
  1. [main]
  2. ...
  3. securityManager.sessionManager.sessionValidationSchedulerEnabled = false
关闭无效session的删除(默认是开启)
    
    
  1. main]
  2. ...
  3. securityManager.sessionManager.deleteInvalidSessions = false
在web应用中,如果是在获取会话时验证了会话已过期,将抛出InvalidSessionException;因此需要捕获这个异常并跳转到相应的页面告诉用户会话已过期,让其重新登录,如可以在web.xml配置相应的错误页面:
    
    
  1. <error-page>
  2. <exception-type>org.apache.shiro.session.InvalidSessionException</exception-type>
  3. <location>/invalidSession.jsp</location>
  4. </error-page>

对于shiro实现集群功能,后续文章会介绍。


  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值