笔者在工作中,碰到一个需求,需要根据不同的用户来设置不同的session过期时间。
搜索了半天之后,主要都是在web.xml中通过设置session-timeout参数来设置过期时间,如下所示:
15
但是这样设置的,所有的session过期时间都是一样的。
如何设置不同的呢?网上也有答案,代码如下:
session.setMaxInactiveInterval(30*60);//以秒为单位
但是大家对这个参数的设置想法都不统一,有的说是全量的session,有的说是单独设置每个session的,也没有人能从源码角度来说明下,所以,笔者干脆自己下了tomcat的源码,源码在手,答案我有。
特意把这个过程整理为本文。
1.tomcat8.5源码下载导入笔者公司使用的是tomcat8,所以特地下载了8.5的代码,笔者不需要编译,只需要把项目所需要的jar包引入下来即可。
由于其使用的是ant编译,笔者不太熟悉,所以就使用maven的方式引入包了。
1.1 创建pom.xml我们主动在%tomcat_home%包路径下创建一个pom.xml,内容如下:
4.0.0
org.apache.tomcat
Tomcat8.5
Tomcat8.5
8.5
Tomcat8.5
java
java
org.apache.maven.plugins
maven-compiler-plugin
3.1
UTF-8
1.8
1.8
junit
junit
4.12
test
org.easymock
easymock
3.4
ant
ant
1.7.0
wsdl4j
wsdl4j
1.6.2
javax.xml
jaxrpc
1.1
org.eclipse.jdt.core.compiler
ecj
4.5.1
1.2 导入源码
导入就比较简单了,笔者使用idea导入,直接使用File -> Open 当前项目即可
pom.xml文件也通过Add Maven Project的方式引入进来,再点击Reimport即可,所需要的包都引入了下来。如下图所示:
2.HttpSession
tomcat中有关Session的接口是HttpSession,找到其实现类,默认为StandardSession。
2.1 StandardSessionpublic class StandardSession implements HttpSession, Session, Serializable {
// session的相关属性集合
protected ConcurrentMap attributes = new ConcurrentHashMap();
// session创建时间
protected long creationTime = 0L;
// session是否过期
protected transient volatile boolean expiring = false;
// 唯一标识 id
protected String id = null;
// 这个参数比较重要,表示当前session最后一个被访问的时间,默认为创建时间
protected volatile long lastAccessedTime = creationTime;
// 这个就是我们要寻找的参数,后续介绍
protected volatile int maxInactiveInterval = -1;
// 当前访问时间 TODO
protected volatile long thisAccessedTime = creationTime;
}
2.2 session过期判断
public class StandardSession implements HttpSession, Session, Serializable {
// 判断当前session是否有效
public boolean isValid() {
if (!this.isValid) {
return false;
}
if (this.expiring) {
return true;
}
if (ACTIVITY_CHECK && accessCount.get() > 0) {
return true;
}
// 主要在这里
// 如果用户设置了maxInactiveInterval参数,则需要判断
// 如果设置maxInactiveInterval=-1,说明永不过期
if (maxInactiveInterval > 0) {
// 获取session空闲时间
int timeIdle = (int) (getIdleTimeInternal() / 1000L);
// 如果距离最后一个访问时间已经超过了maxInactiveInterval,则将session置为过期
if (timeIdle >= maxInactiveInterval) {
expire(true);
}
}
return this.isValid;
}
public long getIdleTimeInternal() {
// 主要就是拿当前时间与最后一次访问时间做比较lastAccessedTime
long timeNow = System.currentTimeMillis();
long timeIdle;
if (LAST_ACCESS_AT_START) {
timeIdle = timeNow - lastAccessedTime;
} else {
timeIdle = timeNow - thisAccessedTime;
}
return timeIdle;
}
}
所以通过源码可以知道,session是否过期的判断,主要是当前时间与session最后一次被访问时间之间的差值是否大于设置的maxInactiveInterval。
那么通过对不同的session设置不同的maxInactiveInterval是可以定制化session的过期时间的。
2.2 request与session在request.getSession()获取session的时候,session的访问时间就已经被修改了。
public class Request implements HttpServletRequest {
public HttpSession getSession() {
Session session = doGetSession(true);
if (session == null) {
return null;
}
return session.getSession();
}
protected Session doGetSession(boolean create) {
...
Manager manager = context.getManager();
if (manager == null) {
return null; // Sessions are not supported
}
if (requestedSessionId != null) {
try {
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("request.session.failed", requestedSessionId, e.getMessage()), e);
} else {
log.info(sm.getString("request.session.failed", requestedSessionId, e.getMessage()));
}
session = null;
}
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
// 设置session 访问时间,具体见2.2.1
session.access();
return session;
}
}
...
}
}
2.2.1 StandardSession.access()
public class StandardSession implements HttpSession, Session, Serializable {
public void access() {
// 将当前session访问时间重置为当前时间
this.thisAccessedTime = System.currentTimeMillis();
if (ACTIVITY_CHECK) {
accessCount.incrementAndGet();
}
}
}
访问结束后,会调用StandardSession.endAccess()方法,如下所示:
public class StandardSession implements HttpSession, Session, Serializable {
public void endAccess() {
isNew = false;
// 重置lastAccessedTime和thisAccessedTime
if (LAST_ACCESS_AT_START) {
this.lastAccessedTime = this.thisAccessedTime;
this.thisAccessedTime = System.currentTimeMillis();
} else {
this.thisAccessedTime = System.currentTimeMillis();
this.lastAccessedTime = this.thisAccessedTime;
}
if (ACTIVITY_CHECK) {
accessCount.decrementAndGet();
}
}
}
总结:
通过调用Session.setMaxInactiveInterval()方法可以自定义不同session的过期时间,且只对当前session有效。