shiro session设置了过期时间不起作用、无效;

背景知识

虽然本文主要是讲述shiro设置session过期时间无效问题,但是shiro的sesssion由sessionmanager管理,所以这里有必要介绍一下sessionmanager,这样有助于理解。shiro常用的sessionmanager有ServletContainerSessionManagerDefaultWebSessionManager

  • servlet container session
    设置为ServletContainerSessionManager时,session的操作由servlet容器(tomcat、jetty)负责,简单来说shiro只起桥接作用,这时的session过期时间,在web.xml中的sessiontimeout中指定;

    <session-config>
      <!-- web.xml expects the session timeout in minutes: -->
      <session-timeout>30</session-timeout>
    </session-config>
    
  • native session
    设置为 DefaultWebSessionManager时,session是native session,也就是本地session,由shiro管理session(创建、更新、销毁),跟servlet容器没有关系。这种情况时,session的过期时间在shiro配置文件中指定。

bug再现

通常我们都会选择shiro自己管理session,shiro配置配置如下


	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="cacheManager" ref="ehCacheManager" />
		<property name="sessionManager" ref="sessionManager"></property>
	</bean>
	<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
		<property name="globalSessionTimeout" value="1800000"></property>
		<property name="sessionDAO" value="enterpriseCacheSessionDAO"></property>
	</bean>
  <bean id="enterpriseCacheSessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"/>
  <bean id="ehCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"/>

这里过期时间指定为1800000毫秒=30分钟*60秒*1000毫秒,也就是30分钟。
EnterpriseCacheSessionDAO的父类CachingSessionDAO,定义了缓存session的缓存块名字

public abstract class CachingSessionDAO extends AbstractSessionDAO implements CacheManagerAware {

    /**
     * The default active sessions cache name, equal to {@code shiro-activeSessionCache}.
     */
    public static final String ACTIVE_SESSION_CACHE_NAME = "shiro-activeSessionCache";
    }

所以ehcache的通常会有shiro-activeSessionCache块,配置如下

<cache name="shiro-activeSessionCache" 
maxEntriesLocalHeap="0" 
eternal="false" 
timeToIdleSeconds="120" 
timeToLiveSeconds="1800" 
overflowToDisk="false"/>

这样配置有2个错误。

以上两种错误,都是ehcache自作主张导致session未过期而删除,未通知session调度器。正确的做法是只有session调度器 有权删除,ehcache不能自行删除,这就要求ehcache中session的空闲时间为永久、如果内存不够就写到磁盘,如果要求重启jvm session不丢失,还需要diskPersistent=true。顺便说一下,session调度器默认是每隔1小时,执行一次。
正确的配置应该为

<cache name="shiro-activeSessionCache" 
maxEntriesLocalHeap="0"  <!--内存中不限制数量-->
eternal="true"  <!--永久-->
timeToIdleSeconds="0" <!--空闲时间为永久-->
timeToLiveSeconds="0" <!--存活时间为永久-->
overflowToDisk="true" <!--如果内存不够则写到磁盘,持久化以防止session丢失-->
diskPersistent="true" <!--重启jvm,session不丢失-->
/>

其实在shiro-ehcache.jar中,有个ehcache.xml(对应java中的net.sf.ehcache.config.CacheConfiguration),里边的注释也明确说明了这个问题

 <!-- We want eternal="true" and no timeToIdle or timeToLive settings because Shiro manages session
         expirations explicitly.  If we set it to false and then set corresponding timeToIdle and timeToLive properties,
         ehcache would evict sessions without Shiro's knowledge, which would cause many problems
        (e.g. "My Shiro session timeout is 30 minutes - why isn't a session available after 2 minutes?"
               Answer - ehcache expired it due to the timeToIdle property set to 120 seconds.)
  
        diskPersistent=true since we want an enterprise session management feature - ability to use sessions after
        even after a JVM restart.  -->
    <cache name="shiro-activeSessionCache"
           maxElementsInMemory="10000"
           overflowToDisk="true"
           eternal="true"
           timeToLiveSeconds="0"
           timeToIdleSeconds="0"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"/>

总结

  • 设置session过期时间不起作用,是因为ehcache自行删除导致;
  • redis/ehcache与session的运作机制搞懂了。缓存只用来存储session,不能自行删除,session调度器负责check session有效性,如果过期,则明确指示redis/ehcache删除session。

参考

shiro session management

发布了336 篇原创文章 · 获赞 369 · 访问量 193万+

猜你喜欢

转载自blog.csdn.net/wangjun5159/article/details/89875746