JSP Session 完整教程

目录

  1. 什么是 Session?
  2. 为什么需要 Session?
  3. Session 的工作原理
  4. JSP 中如何使用 Session?
    • 获取 Session 对象
    • 存储数据到 Session
    • 从 Session 获取数据
    • 移除 Session 中的数据
    • 销毁整个 Session
  5. 一个完整的登录验证示例
  6. Session 的生命周期和超时
  7. Session vs. Cookie
  8. 最佳实践与注意事项

什么是 Session?

在 Web 应用中,Session(会话) 是一种服务器端的技术,用于在多个页面请求之间保存用户的状态信息

jsp session教程
(图片来源网络,侵删)

你可以把它想象成一个“用户专属的临时储物柜”,当一个用户访问你的网站时,服务器会为他创建一个独一无二的 Session 对象,并分配一个唯一的 ID(称为 Session ID),之后,该用户在同一个浏览器会话中访问的所有页面,都可以共享这个“储物柜”里的东西。

核心特点:

  • 服务器端存储:数据存储在服务器上,客户端(浏览器)只持有 Session ID。
  • 基于 Cookie:默认情况下,Session ID 通过 Cookie 在浏览器和服务器之间传递。
  • 有生命周期:Session 不是永久的,它会在一段时间后自动失效(超时),或者可以被手动销毁。
  • 用户隔离:每个用户的 Session 都是独立的,互不干扰。

为什么需要 Session?

HTTP 协议本身是无状态的,这意味着服务器不会记住任何关于前一个请求的信息,每次请求都是全新的、独立的。

  1. 用户登录成功(请求 A)。
  2. 服务器验证用户名密码,告诉他“登录成功”。
  3. 用户点击“个人中心”链接(请求 B)。
  4. 服务器收到请求 B,它不知道这个用户是谁,也不知道他刚刚登录过,他需要再次验证身份。

Session 就是为了解决这个问题而生的。 在请求 A 中,服务器将“用户已登录”这个状态信息存入 Session,当请求 B 到达时,服务器通过请求中携带的 Session ID 找到对应的 Session,发现里面有“用户已登录”的信息,于是直接允许他访问个人中心,无需再次登录。

jsp session教程
(图片来源网络,侵删)

常见的 Session 应用场景:

  • 用户登录状态管理:记录用户是否已登录。
  • 购物车:在用户浏览不同商品页面时,保存他已选择的商品。
  • 存储用户偏好设置:如语言、主题等。
  • 防止表单重复提交:在提交后,将一个标记存入 Session,页面刷新时检查该标记。

Session 的工作原理

  1. 创建 Session:当某个 JSP 页面第一次调用 request.getSession() 方法时(或类似的 API),服务器会检查请求中是否包含有效的 Session ID。

    • 如果没有,服务器会创建一个新的 HttpSession 对象,并生成一个唯一的 Session ID(A1B2C3D4E5F6)。
    • 如果有,服务器会根据这个 ID 查找对应的 Session 对象,如果找到且未超时,则直接使用该对象。
  2. 传递 Session ID:服务器会将这个 Session ID 通过 Cookie 发送给客户端,并设置 Cookie 的名称通常为 JSESSIONID,浏览器会自动保存这个 Cookie。

  3. 后续请求:在同一个浏览器会话中,用户之后的每一次请求(点击链接、刷新页面等),浏览器都会自动带上这个 JSESSIONID Cookie。

  4. 服务器识别:服务器收到后续请求后,会读取 JSESSIONID Cookie,并根据这个 ID 找到之前创建的 Session 对象,从而实现跨请求的状态保持。

注意:如果用户禁用了浏览器 Cookie,默认的 Session 机制会失效,但 Tomcat 等服务器会提供一种 URL 重写(URL Rewriting)的机制作为备选,即 Session ID 会直接附加在 URL 后面(index.jsp;jsessionid=A1B2C3D4E5F6)。


JSP 中如何使用 Session?

在 JSP 中,session 是一个内置对象,类型为 javax.servlet.http.HttpSession,你无需手动创建它,可以直接使用。

1 获取 Session 对象

虽然可以直接使用 session 内置对象,但为了清晰,也可以显式获取: HttpSession session = request.getSession();

  • request.getSession(true) (或 request.getSession()):Session 不存在,会创建一个新的,这是最常用的方式。
  • request.getSession(false):Session 不存在,则返回 null,不会创建新的。

2 存储数据到 Session

使用 session.setAttribute(String name, Object value) 方法。name 是一个字符串键,value 是任意 Java 对象。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>设置 Session 数据</title>
</head>
<body>
    <h1>设置 Session 数据</h1>
    <%
        // 1. 获取 session 对象 (如果不存在会自动创建)
        HttpSession session = request.getSession();
        // 2. 存储数据
        String username = "张三";
        session.setAttribute("username", username);
        // 也可以存储更复杂的数据,比如一个 User 对象
        // User user = new User("李四", "user@example.com");
        // session.setAttribute("user", user);
        out.println("用户名 '" + username + "' 已存入 Session。");
    %>
    <br>
    <a href="get_session_data.jsp">点击此处获取 Session 数据</a>
</body>
</html>

3 从 Session 获取数据

使用 session.getAttribute(String name) 方法,它会返回一个 Object 对象,所以需要进行类型转换。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>获取 Session 数据</title>
</head>
<body>
    <h1>获取 Session 数据</h1>
    <%
        // 从 session 中获取数据
        Object usernameObj = session.getAttribute("username");
        if (usernameObj != null) {
            // 需要进行类型转换
            String username = (String) usernameObj;
            out.println("从 Session 中获取到的用户名是: <strong>" + username + "</strong>");
        } else {
            out.println("Session 中没有找到 'username' 属性。");
        }
    %>
</body>
</html>

4 移除 Session 中的特定数据

使用 session.removeAttribute(String name) 方法,这只会删除指定的属性,Session 对象本身仍然存在。

<%
    session.removeAttribute("username");
    out.println("Session 中的 'username' 属性已被移除。");
%>

5 销毁整个 Session

使用 session.invalidate() 方法,这会立即销毁当前 Session 对象,并清除其中所有绑定的数据,通常用于“安全退出”功能。

<%
    // 销毁整个 session
    session.invalidate();
    out.println("Session 已销毁,您已安全退出。");
    // 通常销毁后会重定向到登录页面或首页
    // response.sendRedirect("login.jsp");
%>

一个完整的登录验证示例

这个例子将展示如何结合 Session 和表单实现一个简单的登录功能。

文件结构:

/your-project
|-- login.jsp          (登录页面)
|-- dashboard.jsp      (需要登录才能访问的页面)
|-- logout.jsp         (登出页面)
|-- WEB-INF/
    |-- web.xml

login.jsp (登录表单)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>登录</title>
</head>
<body>
    <h1>用户登录</h1>
    <%-- 检查是否已有登录信息,有则提示 --%>
    <% if (session.getAttribute("username") != null) { %>
        <p style="color: red;">您已经登录了!</p>
    <% } %>
    <form action="login_check.jsp" method="post">
        用户名: <input type="text" name="username" required><br>
        密码:   <input type="password" name="password" required><br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

login_check.jsp (后台验证逻辑)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    request.setCharacterEncoding("UTF-8");
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    // 简单的硬编码验证
    if ("admin".equals(username) && "password123".equals(password)) {
        // 验证成功,将用户名存入 session
        session.setAttribute("username", username);
        out.println("<script>alert('登录成功!'); window.location.href='dashboard.jsp';</script>");
    } else {
        // 验证失败,返回登录页面
        out.println("<script>alert('用户名或密码错误!'); window.location.href='login.jsp';</script>");
    }
%>

dashboard.jsp (受保护页面)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>用户中心</title>
</head>
<body>
    <%
        // 检查 session 中是否存在 username 属性
        if (session.getAttribute("username") == null) {
            // 如果不存在,说明用户未登录,重定向到登录页
            response.sendRedirect("login.jsp");
            return; // 停止执行后续代码
        }
    %>
    <h1>欢迎, <%= session.getAttribute("username") %>!</h1>
    <p>这是一个受保护的页面,只有登录用户才能看到。</p>
    <a href="logout.jsp">安全退出</a>
</body>
</html>

logout.jsp (登出逻辑)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    // 销毁 session
    session.invalidate();
    // 重定向到登录页
    response.sendRedirect("login.jsp");
%>

Session 的生命周期和超时

  • 创建:第一次调用 request.getSession() 时创建。
  • 活动:有客户端请求与该 Session 关联时,Session 处于活动状态。
  • 超时:如果在一个指定的时间内,客户端没有发出任何与该 Session 关联的请求,服务器会认为该 Session 已闲置,并自动将其销毁,这个时间就是超时时间
  • 销毁:可以通过 session.invalidate() 手动销毁,或者超时后自动销毁。

如何设置 Session 超时时间?

  1. 在 JSP/Servlet 中设置(单位:秒)

    <%-- 设置 Session 超时时间为 30 分钟 (1800秒) --%>
    <%
        session.setMaxInactiveInterval(1800);
    %>
  2. web.xml 中全局设置(推荐) 这会影响整个应用中所有的 Session。

    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <!-- 设置 Session 超时时间为 30 分钟 -->
        <session-config>
            <session-timeout>30</session-timeout>
        </session-config>
    </web-app>

Session vs. Cookie

特性 Session Cookie
存储位置 服务器端 客户端 (浏览器)
安全性 ,敏感数据不经过网络。 ,数据存储在客户端,可能被窃取或篡改。
存储容量 ,只受服务器内存限制。 ,通常限制在 4KB 左右。
数据类型 可以存储任意 Java 对象。 通常只能存储字符串。
生命周期 可配置超时时间,也可手动销毁。 可以设置为会话级(关闭浏览器失效)或持久化(长期有效)。
依赖关系 通常依赖 Cookie 来传递 Session ID。 独立,不依赖其他技术。
  • Session 适合存储敏感、重要、量大的用户状态数据,如登录信息、购物车。
  • Cookie 适合存储少量、非敏感、需要长期保存的数据,如用户的主题偏好、上次访问时间等。

最佳实践与注意事项

  1. 及时清理:当不再需要 Session 中的数据时(例如用户登出后),使用 session.invalidate()session.removeAttribute() 及时清理,避免占用服务器内存。
  2. 避免存储大量数据:Session 存储在服务器内存中,如果每个用户都存大量数据,会迅速消耗服务器资源,影响性能,对于大型数据,应考虑数据库或缓存(如 Redis)。
  3. 注意线程安全:在一个多线程的 Web 容器中,一个 HttpSession 对象不能被多个线程同时修改,如果多个请求(如多个标签页)同时修改同一个 Session 的属性,可能会导致数据不一致,应避免在 Session 中存储可变的状态。
  4. 处理禁用 Cookie 的情况:如果你的应用必须支持禁用 Cookie 的浏览器,确保你的链接是 Session 友好的,虽然 Tomcat 默认会处理 URL 重写,但在手动构造链接时,可以使用 response.encodeURL() 来自动附加 Session ID。
    <a href="<%= response.encodeURL("protected_page.jsp") %>">受保护的链接</a>
  5. 不要滥用 Session:不是所有数据都需要存入 Session,只存储真正需要在多个请求间共享的状态信息。

希望这份详细的教程能帮助你完全理解和使用 JSP Session!