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

1、Shiro会话管理的特性

    (1)会话事件监听:提供对对session整个生命周期的监听。

     (2)  会话存储/持久化:因为shiro中的session对象是基于java对象的,所以可以将session存储在任何地方,例如,文件,各种数据库,内存中等。

    (3)容器无关的集群功能:shiro中的session可以很容易的集成第三方的缓存产品完成集群的功能。例如,Ehcache + Terracotta, Coherence, GigaSpaces等。

    (4)会话失效/过期的支持:用户长时间处于不活跃状态可以使会话过期,调用touch()方法,可以主动更新最后访问时间,让会话处于活跃状态。

    (5)透明的Web支持:shiro全面支持Servlet 2.5中的session规范。可以将现有的web程序改为shiro会话,无需修改代码。

    (6)单点登录的支持:shiro session基于普通java对象,更容易存储和共享,可以实现跨应用程序共享。可以根据共享的会话,来保证认证状态到另一个程序。从而实现单点登录。

2、会话管理器

    SessionManager管理所有Subject的session包括创建、维护、删除、失效、验证等工作。

    SessionManager是顶层组件,由SecurityManager管理。

SecurityManager的实现类DefaultSecurityManager及DefaultWebSecurityManager继承了SessionsSecurityManager。

SessionsSecurityManager可以把相应的会话管理委托给SessionManager。

Shiro提供了三个默认实现

  • (1)DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于JavaSE环境。
  • (2)ServletContainerSessionManager:DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话。
  • (3)DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager,自行维护会话,直接废弃Servlet容器的会话管理。

替换SecurityManager默认的SessionManager可以在ini中配置(shiro.ini)

[main]sessionManager=org.apache.shiro.web.session.mgt.ServletContainerSessionManagersecurityManager.sessionManager=$sessionManager

可以设置会话的全局过期时间(毫秒为单位),默认30分钟:

sessionManager. globalSessionTimeout=1800000;

默认情况下globalSessionTimeout将应用给所有Session。可以单独设置每个Session的timeout属性来为每个Session设置其超时时间。

如果使用ServletContainerSessionManager进行会话管理,Session的超时依赖于底层Servlet容器的超时时间,可以在web.xml中配置其会话的超时时间(分钟为单位):

<session-config>  <session-timeout>30session-timeout>session-config>

在Servlet容器中,默认使用JSESSIONID Cookie维护会话,且会话默认是跟容器绑定的。在某些情况下可能需要使用自己的会话机制,此时我们可以使用DefaultWebSessionManager来维护会话。

// 创建会话Cookie的模板sessionIdCookie=org.apache.shiro.web.servlet.SimpleCookiesessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager// 设置Cookie名字,默认为JSESSIONIDsessionIdCookie.name=sid// 设置Cookie的域名,默认空,即当前访问的域名#sessionIdCookie.domain=sishuok.com// 设置Cookie的路径,默认空,即存储在域名根下#sessionIdCookie.path=// 设置Cookie的过期时间,秒为单位,默认-1表示关闭浏览器时过期CookiesessionIdCookie.maxAge=1800// 如果设置为true,则客户端不会暴露给客户端脚本代码,使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击;此特性需要实现了Servlet 2.5 MR6及以上版本的规范的Servlet容器支持;sessionIdCookie.httpOnly=truesessionManager.sessionIdCookie=$sessionIdCookie// 是否启用/禁用Session Id Cookie,默认是启用的;如果禁用后将不会设置Session Id Cookie,即默认使用了Servlet容器的JSESSIONID,且通过URL重写(URL中的“;JSESSIONID=id”部分)保存Session Id。sessionManager.sessionIdCookieEnabled=truesecurityManager.sessionManager=$sessionManager;

三、会话存储

Shiro提供SessionDAO用于会话的CRUD。

//如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();




ager.sessionDAO=$sessionDAO;




Shiro提供了使用Ehcache进行会话存储,Ehcache可以配合TerraCotta实现容器无关的分布式集群。

<dependency>    <groupId>org.apache.shirogroupId>    <artifactId>shiro-ehcacheartifactId>    <version>1.2.2version>dependency>

接着配置shiro-web.ini文件

//如果自定义了sessionDAO,则需要改成自己的sessionDAO路径//自定义的SessionDAO需要继承继承CachingSessionDAO,以便实现缓存效果)sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO// 设置Session缓存名字,默认就是shiro-activeSessionCachesessionDAO. activeSessionsCacheName=shiro-activeSessionCachesessionManager.sessionDAO=$sessionDAO// 缓存管理器,用于管理缓存的,此处使用Ehcache实现cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager// 设置ehcache缓存的配置文件cacheManager.cacheManagerConfigFile=classpath:ehcache.xml// 设置SecurityManager的cacheManager,会自动设置实现了CacheManagerAware接口的相应对象,如SessionDAO的cacheManagersecurityManager.cacheManager = $cacheManager;

会话ID的生成可以添加以下配置:(默认是JavaUuidSessionIdGenerator,使用java.util.UUID生成)

sessionIdGenerator=org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGeneratorsessionDAO.sessionIdGenerator=$sessionIdGenerator;

然后配置ehcache.xml:

<cache name="shiro-activeSessionCache"       maxEntriesLocalHeap="10000"       overflowToDisk="false"        eternal="true"       diskPersistent="false"       timeToLiveSeconds="0"       timeToIdleSeconds="0"       statistics="true"/>;

name属性的配置跟shiro-web.ini文件中的配置一致。

overflowToDisk="false"  : true表示如果内存不够用了,会将会话保存到磁盘。

eternal=”true” :设置为true,表示会话对象的缓存不会被自动设置为过期或删除。shiro的session检查机制是基于调度器定时检测的,如果自动删除或者设置过期的话,shiro是无法知道session是否过期的,这样就会出现问题,所以要设置为true。

四、会话监听

监听会话创建、过期及停止事件

shiro 不需要session_shiro 修改sessionid

在shiro-web.ini文件中配置会话监听器:

sessionListener1=com.example.demo.listener.MySessionListenersessionListener2=com.example.demo.listener.MySessionListener2sessionManager.sessionListeners=$sessionListener1,$sessionListener2

五、会话验证

一般情况下都是获取会话时来验证会话是否过期并停止会话的,但在web环境中,如果用户不主动退出无法知道会话是否过期,需要定期检测会话是否过期,Shiro提供了会话验证调度器SessionValidationScheduler来检测。

在shiro-web.ini文件中开启会话验证:

// 会话验证调度器,sessionManager默认就是使用ExecutorServiceSessionValidationScheduler,其使用JDK的ScheduledExecutorService进行定期调度并验证会话是否过期sessionValidationScheduler=org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler// 设置调度时间间隔,单位毫秒,默认就是1小时sessionValidationScheduler.interval = 3600000// 设置会话验证调度器进行会话验证时的会话管理器sessionValidationScheduler.sessionManager=$sessionManager// 设置全局会话超时时间,默认30分钟,即如果30分钟内没有访问会话将过期sessionManager.globalSessionTimeout=1800000// 是否开启会话验证器,默认是开启的sessionManager.sessionValidationSchedulerEnabled=true// 设置会话验证调度器,默认就是使用ExecutorServiceSessionValidationSchedulersessionManager.sessionValidationScheduler=$sessionValidationScheduler;

如果在会话过期时不想删除过期的会话(默认是开启的),可以通过如下ini配置进行设置:

sessionManager.deleteInvalidSessions=false

Shiro也提供了使用Quartz会话验证调度器,导入shiro-quartz依赖:

<dependency>     <groupId>org.apache.shirogroupId>     <artifactId>shiro-quartzartifactId>     <version>1.2.2version>dependency>

然后在ini配置文件中进行配置:

sessionValidationScheduler=org.apache.shiro.session.mgt.quartz.QuartzSessionValidationSchedulersessionValidationScheduler.sessionValidationInterval = 3600000sessionValidationScheduler.sessionManager=$sessionManager;

如果是在获取会话时验证了会话已过期,将抛出InvalidSessionException;因此需要捕获这个异常并跳转到相应的页面告诉用户会话已过期,让其重新登录,可以在web.xml配置相应的错误页面。

验证过程源码:

(1)ExecutorServiceSessionValidationScheduler 的run方法:

shiro 不需要session_shiro 修改sessionid_02

(2)首先需要获取所有会话(调用sessionDAO,会先检查缓存)

shiro 不需要session_shiro 单点登录_03

shiro 不需要session_shiro 不需要session_04

(3)然后进行单个session验证,调用ValidatingSession的实现类中的验证方法:

shiro 不需要session_shiro 不需要session_05

shiro 不需要session_shiro 不需要session_06

先检查是不是已经停止会话,如果已经停止会话就返回停止信息,然后再进行时间戳的比较,当前时间与设置的过期时间比较:

timeout:设置的过期时间

lastAccessTime:最后一次会话连接(调用)开始时间。

expireTimeMillis:当前时间戳-设置的过期时间

shiro 不需要session_shiro 修改sessionid_07