背景
我在shiro session设置了过期时间不起作用、无效 博文中,提到无论是否交互,到了30分钟就会失效,一位网友提出不同意见,并给出链接
关于Ehcache缓存中timeToLiveSeconds和timeToIdleSeconds ,网友提到
补充下,shiro里有AbstractShiroFilter在收到请求后会自己更新维护session的最后一次操作时间,所以 作者的 :所以无论30分钟内,用户是否产生交互,只要到了30分钟,ehcache就会删除session!!! 是不正确的.
经过一番研究,证实我的博文中描述错误,网友所说正确。
先上结论,shiro在更新session时,也会更新ehcahce的创建时间,所以如果有交互,session会延迟过期时间。
其实长久以来,我对ehcache的过期时间也迷迷糊糊,尤其是timeToLiveSeconds、timeToIdleSeconds的关系,如果两个同时设置哪个起作用?网上众说纷纭,莫衷一是,为了获取最准确的答案,决定看源码
maven配置文件
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
首先要明确一个前提,eternal=false时,timeToLiveSeconds、timeToIdleSeconds才起作用。以下讨论是基于eternal=false这个大前提下,我们先看一下ehache怎么计算过期时间的
net.sf.ehcache.Element
/**
* Returns the expiration time based on time to live. If this element also has a time to idle setting, the expiry
* time will vary depending on whether the element is accessed.
*
* @return the time to expiration
*/
public long getExpirationTime() {
if (!isLifespanSet() || isEternal()) {
return Long.MAX_VALUE;
}
long expirationTime = 0;
long ttlExpiry = creationTime + TimeUtil.toMillis(getTimeToLive());
long mostRecentTime = Math.max(creationTime, lastAccessTime);
long ttiExpiry = mostRecentTime + TimeUtil.toMillis(getTimeToIdle());
if (getTimeToLive() != 0 && (getTimeToIdle() == 0 || lastAccessTime == 0)) {
expirationTime = ttlExpiry;
} else if (getTimeToLive() == 0) {
expirationTime = ttiExpiry;
} else {
expirationTime = Math.min(ttlExpiry, ttiExpiry);
}
return expirationTime;
}
根据源码可知
- 如果设置了TTL,并且TTI为永久空闲或者未被访问过,那么过期时间是ttlExpiry(创建时间+TTL)
- 如果未设置TTL(TTL=0,永久存活),那么过期时间是ttiExpiry,也就是max(创建时间,最后一次访问时间)+TTI
- 其它情况,取 min(ttlExpiry,ttiExpiry)
以上是ehcache的过期时间计算方法,但是shiro在更新session时,会同步更新Element的createTime,所以每次访问后,创建时间会自动延迟。看源码
CachingSessionDAO.java
public void update(Session session) throws UnknownSessionException {
doUpdate(session);
if (session instanceof ValidatingSession) {
if (((ValidatingSession) session).isValid()) {
cache(session, session.getId());
} else {
uncache(session);
}
} else {
cache(session, session.getId());
}
}
org.apache.shiro.cache.ehcache.EhCache
/**
* Puts an object into the cache.
*
* @param key the key.
* @param value the value.
*/
public V put(K key, V value) throws CacheException {
if (log.isTraceEnabled()) {
log.trace("Putting object in cache [" + cache.getName() + "] for key [" + key + "]");
}
try {
V previous = get(key);
//创建新的Element,更新createTime
Element element = new Element(key, value);
cache.put(element);
return previous;
} catch (Throwable t) {
throw new CacheException(t);
}
}
shiro更新session时,调用了cache方法,最终调用了Ehcache的put方法,创建新的Element,取当前时间为创建时间。也就是说,shiro在更新session时,更改了ehcache中Element的创建时间,从而延长了存活时间。
现在举例说明,
1分钟时创建,TTL=4分钟,TTI =2分钟,最后一次访问时间是3分钟,那么TTLExpire=7分钟,TTIExpire=5分钟,过期时间是5分钟!!
再反过来
1分钟时创建,TTL=2分钟,TTI =4分钟,最后一次访问时间是1分钟,TTLExpire=3分钟,TTIExpire=5分钟,那么过期时间是3分钟。
总结
- eternal=true时
- timeToLiveSeconds、timeToIdleSeconds都不起作用,即使设置了也枉然
- eternal=false时,
- timeToLiveSeconds、timeToIdleSeconds起作用,TTLExpire=createTime+TTL,TTIExpire=max(creatTime,accessTime)+TTI,过期时间为min(TTLExpire,TTIExpire)