Session 是 Web 应用中非常重要的一种机制,用于在多个 HTTP 请求之间保持用户的状态信息,由于 HTTP 协议本身是无状态的,所以我们需要一个机制来管理这个“会话的生命周期”,而“过期”就是这个生命周期管理的核心部分。

什么是 Session 过期?
Session 过期指的是一个已经建立的 Session,由于长时间未被使用或超过了其预设的生命周期,被服务器自动销毁的过程,一旦 Session 过期,存储在其中的所有数据都将丢失,用户需要重新登录或重新建立会话才能恢复其状态。
为什么需要 Session 过期?
- 安全性:防止用户在公共电脑上忘记退出后,他人可以继续使用其身份进行操作,这是最重要的原因。
- 资源释放:Session 在服务器端会占用内存(或存储空间)来存储数据,Session 永不过期,大量无效的、僵尸的 Session 会耗尽服务器资源,导致性能下降甚至崩溃。
- 数据一致性:确保用户长时间未操作后,其状态信息是“失效”的,需要重新验证。
Session 过期的两种主要方式
Session 过期可以通过两种方式触发,这两种方式通常是协同工作的。
基于不活动时间的超时(最主要的方式)
这是最常见和最核心的过期机制,服务器会为每个 Session 设置一个“最大不活动时间”(session-timeout),只要用户在这个时间窗口内没有任何请求(即没有调用 request.getSession() 或 request.getSession(true)),该 Session 就会被标记为过期,并在下一次服务器检查时被清理。

如何配置 session-timeout?
配置 session-timeout 的位置取决于你的 Web 服务器和应用类型。
A. 在 web.xml 中配置(Servlet 标准,适用于 Tomcat, Jetty 等)
这是最传统和标准的方法,在 WEB-INF/web.xml 文件中添加以下配置:
<session-config>
<!-- 单位是分钟 -->
<session-timeout>30</session-timeout>
</session-config>
上面的配置表示,如果用户超过 30 分钟没有进行任何操作,其 Session 就会过期。
B. 在 Servlet 3.0+ 的注解中配置
如果你使用的是 Servlet 3.0 或更高版本,可以在你的 Servlet 类上使用 @WebServlet 注解来配置。
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
@WebServlet(urlPatterns = "/myApp", sessionTimeout = 1800) // 单位是秒
public class MyServlet extends HttpServlet {
// ...
}
这里 sessionTimeout = 1800 表示 30 分钟(1800秒)。
C. 在 Spring Boot 中配置
Spring Boot 提供了更灵活的配置方式,通常在 application.properties 或 application.yml 文件中设置。
在 application.properties 中:
# 单位是秒,默认是30分钟 server.servlet.session.timeout=1800
在 application.yml 中:
server:
servlet:
session:
timeout: 1800 # 单位是秒
重要提示:
- 单位:
web.xml中是分钟,而 Spring Boot 和 Servlet 3.0 注解中是秒,这是一个常见的混淆点。 - 最小值:很多 Web 服务器(如 Tomcat)对
session-timeout有一个最小限制(通常是 1 分钟),如果你设置的值小于这个最小值,服务器会自动使用最小值。 HttpSession.setMaxInactiveInterval(int interval):你还可以在 Java 代码中动态设置单个 Session 的超时时间(单位是秒),这个值会覆盖web.xml或配置文件中的全局设置。HttpSession session = request.getSession(); // 设置此 Session 5分钟后过期 session.setMaxInactiveInterval(5 * 60);
基于绝对时间的失效
除了基于不活动时间的超时,Session 还有一个“创建时间”和一个“最后访问时间”,当 Session 的“最后访问时间” + “最大不活动时间” < 当前时间时,Session 就失效了。
更重要的是,你可以通过编程方式立即使一个 Session 失效,而无需等待超时,这在用户“安全退出”或“注销”时非常有用。
如何立即使 Session 失效?
调用 HttpSession 对象的 invalidate() 方法。
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
// 在某个 Servlet 或 Controller 中
public void logout(HttpServletRequest request) {
HttpSession session = request.getSession(false); // false表示如果不存在则不创建
if (session != null) {
// 1. 从 Session 中移除所有属性(可选,但 invalidate() 会做这件事)
// session.removeAttribute("user");
// 2. 销毁整个 Session
session.invalidate();
System.out.println("用户已成功注销,Session 已失效。");
}
}
调用 invalidate() 后,该 Session 对象会立即被销毁,后续任何对该 Session 的访问都会抛出 IllegalStateException。
服务器如何清理过期的 Session?
服务器不会在 Session 过期的瞬间就立即将其从内存中移除,这通常是由一个后台的“过期 Session 清理线程”(在 Tomcat 中是 Manager 组件的一部分)定期执行的。
- 周期性检查:这个线程会每隔一段时间(在 Tomcat 中默认是 60 秒)被唤醒一次。
- 遍历所有 Session:它会检查所有当前活动的 Session。
- 判断过期:对于每个 Session,它会检查其
lastAccessedTime+maxInactiveInterval是否小于当前系统时间。 - 销毁和移除:如果已过期,线程会调用该 Session 的
invalidate()方法,执行销毁逻辑(如触发HttpSessionListener的sessionDestroyed事件),然后从服务器的 Session 存储中移除它。
Session 过期不是实时的,可能会有一个短暂的延迟(最多等于清理线程的检查周期)。
监听 Session 生命周期(HttpSessionListener)
你可以通过实现 HttpSessionListener 接口来监听 Session 的创建和销毁(即过期)事件,这对于统计在线用户数、执行清理任务等非常有用。
示例:
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.util.concurrent.atomic.AtomicInteger;
@WebListener
public class MySessionListener implements HttpSessionListener {
// 用于统计当前在线用户数
private static final AtomicInteger activeSessions = new AtomicInteger();
@Override
public void sessionCreated(HttpSessionEvent se) {
// 当一个 Session 被创建时调用
activeSessions.incrementAndGet();
System.out.println("Session 创建,当前在线人数: " + activeSessions.get());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// 当一个 Session 被销毁(包括过期和调用 invalidate())时调用
activeSessions.decrementAndGet();
System.out.println("Session 销毁,当前在线人数: " + activeSessions.get());
}
public static int getActiveSessionCount() {
return activeSessions.get();
}
}
重要区别:
sessionDestroyed:由invalidate()或 过期清理线程 触发。- 如果你只是调用
session.removeAttribute("user"),不会触发sessionDestroyed事件,Session 本身还存在。
常见问题与最佳实践
问题1:为什么我的 Session 总是很快就过期了?
- 检查配置:确认
web.xml或application.properties中的session-timeout值是否设置正确,并注意单位(分钟 vs 秒)。 - 检查代码:是否有代码调用了
session.invalidate()? - 检查代理/防火墙:某些公司或网络环境的代理、防火墙或负载均衡器可能会在短时间内断开连接,导致 Session 失效,可以配置一个 Session 监听器来打印日志,确认 Session 是被
invalidate()了还是自然过期了。
问题2:如何实现“记住我”(Remember Me)功能? “记住我”功能与 Session 过期是两个概念,标准的 Session 过期机制不适用于“记住我”。
- 实现方式:通常的做法是,当用户勾选“记住我”时,服务器不仅创建一个短期(如30分钟)的 Session,还会生成一个长期的、加密的 Token(有效期7天或30天),并将其存储在浏览器的 Cookie 中。
- 后续访问:用户下次访问时,即使 Session 已过期,浏览器会带上这个 Token,服务器验证 Token 有效后,会重新创建一个新的 Session,从而实现“自动登录”。
最佳实践:
- 合理设置超时时间:根据你的应用安全级别和用户体验来权衡,对于银行类应用,5-15分钟可能比较合适,对于普通内容网站,30分钟到1小时是常见的选择。
- 安全退出时调用
invalidate():在用户注销功能中,务必调用session.invalidate()来立即销毁会话。 - 不要在 Session 中存储敏感信息:Session 存储在服务器端,相对安全,但仍应避免将密码、信用卡号等高度敏感信息明文存入,如果必须存储,应先进行加密。
- 利用监听器进行监控:使用
HttpSessionListener监控在线用户数,可以帮助你了解应用的负载情况。 - 考虑分布式环境:在微服务或集群环境中,Session 需要被共享(例如使用 Redis),这时 Session 过期机制依赖于外部存储系统(如 Redis)的 TTL(Time To Live)功能,其原理与单机版类似,但配置方式不同,Spring Session 是实现这一点的流行框架。
