tomcat session过期时间的设置

前言:

笔者在工作中,碰到一个需求,需要根据不同的用户来设置不同的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有效。

Guess you like

Origin blog.csdn.net/qq_26323323/article/details/121199219