杰瑞科技汇

java session 过期

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

java session 过期-图1
(图片来源网络,侵删)

什么是 Session 过期?

Session 过期指的是一个已经建立的 Session,由于长时间未被使用或超过了其预设的生命周期,被服务器自动销毁的过程,一旦 Session 过期,存储在其中的所有数据都将丢失,用户需要重新登录或重新建立会话才能恢复其状态。

为什么需要 Session 过期?

  1. 安全性:防止用户在公共电脑上忘记退出后,他人可以继续使用其身份进行操作,这是最重要的原因。
  2. 资源释放:Session 在服务器端会占用内存(或存储空间)来存储数据,Session 永不过期,大量无效的、僵尸的 Session 会耗尽服务器资源,导致性能下降甚至崩溃。
  3. 数据一致性:确保用户长时间未操作后,其状态信息是“失效”的,需要重新验证。

Session 过期的两种主要方式

Session 过期可以通过两种方式触发,这两种方式通常是协同工作的。

基于不活动时间的超时(最主要的方式)

这是最常见和最核心的过期机制,服务器会为每个 Session 设置一个“最大不活动时间”(session-timeout),只要用户在这个时间窗口内没有任何请求(即没有调用 request.getSession()request.getSession(true)),该 Session 就会被标记为过期,并在下一次服务器检查时被清理。

java session 过期-图2
(图片来源网络,侵删)

如何配置 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.propertiesapplication.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 组件的一部分)定期执行的。

  1. 周期性检查:这个线程会每隔一段时间(在 Tomcat 中默认是 60 秒)被唤醒一次。
  2. 遍历所有 Session:它会检查所有当前活动的 Session。
  3. 判断过期:对于每个 Session,它会检查其 lastAccessedTime + maxInactiveInterval 是否小于当前系统时间。
  4. 销毁和移除:如果已过期,线程会调用该 Session 的 invalidate() 方法,执行销毁逻辑(如触发 HttpSessionListenersessionDestroyed 事件),然后从服务器的 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.xmlapplication.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,从而实现“自动登录”。

最佳实践:

  1. 合理设置超时时间:根据你的应用安全级别和用户体验来权衡,对于银行类应用,5-15分钟可能比较合适,对于普通内容网站,30分钟到1小时是常见的选择。
  2. 安全退出时调用 invalidate():在用户注销功能中,务必调用 session.invalidate() 来立即销毁会话。
  3. 不要在 Session 中存储敏感信息:Session 存储在服务器端,相对安全,但仍应避免将密码、信用卡号等高度敏感信息明文存入,如果必须存储,应先进行加密。
  4. 利用监听器进行监控:使用 HttpSessionListener 监控在线用户数,可以帮助你了解应用的负载情况。
  5. 考虑分布式环境:在微服务或集群环境中,Session 需要被共享(例如使用 Redis),这时 Session 过期机制依赖于外部存储系统(如 Redis)的 TTL(Time To Live)功能,其原理与单机版类似,但配置方式不同,Spring Session 是实现这一点的流行框架。
分享:
扫描分享到社交APP
上一篇
下一篇