前言:
笔者在工作中,碰到一个需求,需要根据不同的用户来设置不同的session过期时间。
搜索了半天之后,主要都是在web.xml中通过设置session-timeout参数来设置过期时间,如下所示:
<session-config>
<session-timeout>15</session-timeout>
</session-config>
但是这样设置的,所有的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,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>Tomcat8.5</artifactId>
<name>Tomcat8.5</name>
<version>8.5</version>
<build>
<finalName>Tomcat8.5</finalName>
<sourceDirectory>java</sourceDirectory>
<!--<testSourceDirectory>test</testSourceDirectory>-->
<resources>
<resource>
<directory>java</directory>
</resource>
</resources>
<!--<testResources>-->
<!--<testResource>-->
<!--<directory>test</directory>-->
<!--</testResource>-->
<!--</testResources>-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxrpc</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.5.1</version>
</dependency>
</dependencies>
</project>
1.2 导入源码
导入就比较简单了,笔者使用idea导入,直接使用File -> Open 当前项目即可
pom.xml文件也通过Add Maven Project的方式引入进来,再点击Reimport即可,所需要的包都引入了下来。如下图所示:
2.HttpSession
tomcat中有关Session的接口是HttpSession,找到其实现类,默认为StandardSession。
2.1 StandardSession
public class StandardSession implements HttpSession, Session, Serializable {
// session的相关属性集合
protected ConcurrentMap<String, Object> 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有效。