杰瑞科技汇

Java登录session如何管理与保持?

目录

  1. 什么是 Session?
  2. 为什么登录需要 Session?
  3. Session 的工作原理
  4. Java Web 中使用 Session 的核心 API
  5. 实现用户登录的完整流程(代码示例)
  6. 安全最佳实践
  7. Session 与 Cookie 的区别
  8. 替代方案:Token (JWT)

什么是 Session?

Session(会话) 是一种服务器端的机制,用于在多个 HTTP 请求之间保存和共享特定用户的数据。

Java登录session如何管理与保持?-图1
(图片来源网络,侵删)

你可以把它想象成一个“私人储物柜”:

  • 当用户第一次访问网站时,服务器会给他分配一个独一无二的“储物柜”(Session)。
  • 服务器会给用户一张“钥匙”(Session ID),通常通过 Cookie 发送给浏览器。
  • 在接下来的每一次请求中,浏览器都会自动带上这把“钥匙”。
  • 服务器看到“钥匙”后,就知道该打开哪个“储物柜”,取出或存入属于该用户的数据。

这个“储物柜”里的数据就是 HttpSession 对象,我们可以用它来存储用户登录信息、购物车内容等。

为什么登录需要 Session?

HTTP 协议本身是无状态的,这意味着服务器默认不会记住任何用户的请求历史,每次请求都是独立的,服务器不知道上一个请求和当前请求是不是来自同一个用户。

登录功能需要“记忆”用户已经登录的状态,如果没有 Session,用户每点击一个页面,服务器都会认为他是个新访客,要求他重新登录,Session 解决了这个问题,让服务器能够识别出“这是已经登录的用户 A”。

Java登录session如何管理与保持?-图2
(图片来源网络,侵删)

Session 的工作原理

  1. 创建/获取 Session:当用户首次访问一个需要登录的页面时,服务器会检查请求中是否带有 Session ID,如果没有,服务器会创建一个新的 HttpSession 对象,并生成一个唯一的 Session ID。
  2. 发送 Session ID:服务器通过一个名为 JSESSIONID 的 Cookie 将这个 Session ID 发送给浏览器,浏览器会自动保存它。
  3. 后续请求携带 Session ID:在之后的每一次请求中,浏览器都会自动在请求头中带上 JSESSIONID 这个 Cookie。
  4. 服务器识别用户:服务器收到请求后,会从 Cookie 中读取 JSESSIONID,然后根据这个 ID 找到对应的 HttpSession 对象,从而获取到之前存储的用户信息。

Java Web 中使用 Session 的核心 API

在 Java Web 开发中(无论是 Servlet 还是 Spring MVC),我们主要通过 javax.servlet.http.HttpSession 接口来操作 Session。

方法 描述
HttpSession request.getSession() 最常用,获取当前请求的 Session,Session 不存在,则创建一个新的。
HttpSession request.getSession(false) 获取当前请求的 Session,Session 不存在,则返回 null不会创建新 Session
Object HttpSession.getAttribute(String name) 从 Session 中根据键获取值,获取登录用户对象。
void HttpSession.setAttribute(String name, Object value) 向 Session 中存入键值对,将登录用户对象存入 Session。
void HttpSession.removeAttribute(String name) 从 Session 中移除指定的键值对,用户退出登录时移除用户信息。
void HttpSession.invalidate() 销毁整个 Session,会清空 Session 中所有数据,并使 Session ID 失效,这是用户退出登录的标准做法。
long HttpSession.getMaxInactiveInterval() 获取 Session 的超时时间(秒)。
void HttpSession.setMaxInactiveInterval(int interval) 设置 Session 的超时时间(秒),如果超过这个时间没有请求,Session 会自动失效。

实现用户登录的完整流程(代码示例)

这里我们使用最基础的 Servlet 来演示,因为它最能体现底层原理,如果你使用的是 Spring Boot,原理完全一样,只是 API 和配置方式不同。

项目结构

LoginProject
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── example
│       │           ├── controller
│       │           │   ├── LoginServlet.java
│       │           │   └── WelcomeServlet.java
│       │           └── model
│       │               └── User.java
│       └── webapp
│           ├── index.html
│           ├── login.html
│           └── welcome.html

步骤 1: 准备一个简单的 User 模型

User.java

package com.example.model;
public class User {
    private String username;
    private String password;
    // 构造函数、Getter 和 Setter
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
}

步骤 2: 创建登录页面 (login.html)

login.html

<!DOCTYPE html>
<html>
<head>登录</title>
</head>
<body>
    <h2>用户登录</h2>
    <form action="login" method="post">
        用户名: <input type="text" name="username"><br>
        密码: <input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

步骤 3: 编写登录处理逻辑 (LoginServlet.java)

LoginServlet.java

package com.example.controller;
import com.example.model.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    // 模拟一个用户数据库
    private User validUser = new User("admin", "123456");
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取用户名和密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        // 2. 验证用户名和密码 (实际项目中这里应该查询数据库)
        if (validUser.getUsername().equals(username) && validUser.getPassword().equals(password)) {
            // 登录成功
            // 3. 获取 Session (如果不存在会自动创建)
            HttpSession session = request.getSession();
            // 4. 将用户信息存入 Session
            session.setAttribute("user", validUser);
            // 5. 重定向到欢迎页面
            response.sendRedirect("welcome.html");
        } else {
            // 登录失败
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().println("<script>alert('用户名或密码错误!'); window.location.href='login.html';</script>");
        }
    }
}

步骤 4: 创建欢迎页面 (welcome.html)

这个页面需要检查用户是否已登录。

welcome.html

<!DOCTYPE html>
<html>
<head>欢迎页面</title>
</head>
<body>
    <h2>欢迎您!</h2>
    <p>只有登录用户才能看到此页面。</p>
    <p>您可以通过浏览器开发者工具 -> Application -> Cookies 中看到 JSESSIONID。</p>
    <a href="logout">退出登录</a>
</body>
</html>

步骤 5: 创建一个简单的过滤器来检查登录状态

为了保护欢迎页面,我们创建一个过滤器,在访问任何页面之前先检查 Session 中是否有用户信息。

LoginFilter.java (需要创建一个过滤器类)

package com.example.controller;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter("/*") // 过滤所有请求
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // 获取请求的路径
        String path = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
        // 1. 如果是登录页面或登录请求,直接放行
        if (path.equals("/login.html") || path.equals("/login")) {
            chain.doFilter(request, response);
            return;
        }
        // 2. 尝试获取 Session
        HttpSession session = httpRequest.getSession(false); // 不自动创建
        // 3. 检查 Session 中是否有用户信息
        if (session != null && session.getAttribute("user") != null) {
            // 用户已登录,放行
            chain.doFilter(request, response);
        } else {
            // 用户未登录,重定向到登录页面
            httpResponse.sendRedirect("login.html");
        }
    }
    // 其他方法省略...
}

步骤 6: 创建退出登录逻辑 (LogoutServlet.java)

LogoutServlet.java

package com.example.controller;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取当前 Session
        HttpSession session = request.getSession(false);
        // 2. Session 存在,则销毁它
        if (session != null) {
            session.invalidate(); // 这是最关键的,让 Session 失效
        }
        // 3. 重定向到登录页面
        response.sendRedirect("login.html");
    }
}

总结流程:

  1. 用户访问 welcome.htmlLoginFilter 检查到未登录,重定向到 login.html
  2. 用户输入用户名密码,提交到 LoginServlet
  3. LoginServlet 验证成功,将 User 对象存入 session,然后重定向到 welcome.html
  4. 再次访问 welcome.htmlLoginFilter 检查到 session 中有用户信息,放行。
  5. 用户点击“退出登录”,访问 LogoutServletsession.invalidate() 被调用,Session 销毁。
  6. 再次访问 welcome.htmlLoginFilter 检查到 session 为空或无用户信息,再次重定向到登录页。

安全最佳实践

  1. 使用 HTTPS:防止 Session ID 在传输过程中被窃取(Session 劫持)。
  2. 设置合理的 Session 超时时间:在 web.xml 中或通过代码设置,避免用户长时间不操作后 Session 仍然有效。
    <!-- web.xml 中配置 -->
    <session-config>
        <session-timeout>30</session-timeout> <!-- 30分钟 -->
    </session-config>
  3. 重要操作后重新生成 Session ID:在用户登录成功后,可以调用 session.invalidate() 销毁旧 Session,然后重新 request.getSession() 创建一个新 Session,这可以防止“会话固定攻击”。
  4. 不要在 Session 中存储敏感信息:虽然 Session 在服务器端,但如果服务器配置不当,信息可能会泄露,密码等绝对不要明文存储。
  5. 登出时务必销毁 Session:调用 session.invalidate() 是必须的。

Session 与 Cookie 的区别

特性 Cookie Session
存储位置 客户端(浏览器) 服务器端
安全性 较低,数据可能被篡改或窃取 较高,数据存储在服务器,客户端只存 ID
存储容量 通常限制在 4KB 左右 理论上只受服务器内存限制
生命周期 可长期有效(设置过期时间) 通常较短(超时失效)
依赖性 独立存在,不依赖 Session 依赖 Cookie(默认通过 Cookie 传递 ID)

关系:Session 的实现通常依赖于 Cookie,Session ID 是通过 Cookie 发送给客户端的,但 Session ID 也可以通过 URL 重写等方式传递,Cookie 是最方便的方式。

替代方案:Token (JWT)

在现代 Web 开发(尤其是前后端分离架构)中,基于 Session 的机制逐渐被 Token(特别是 JWT - JSON Web Token)所取代。

JWT 的工作方式:

  1. 用户登录成功后,服务器生成一个加密的 Token(字符串),里面包含了用户信息(如用户 ID、角色等)。
  2. 服务器将这个 Token 返回给客户端。
  3. 客户端在后续的每一次请求中,都在请求头(通常是 Authorization: Bearer <token>)中带上这个 Token。
  4. 服务器收到请求后,验证 Token 的合法性(签名是否正确、是否过期等),如果合法,就解析出用户信息,处理请求。

JWT vs Session 的优势:

  • 无状态:服务器不需要存储 Session,减轻了服务器压力,这对于分布式系统尤其重要,因为 Session 需要共享(通常用 Redis 等中间件)。
  • 跨域友好:Token 可以轻松地在不同域名、不同客户端(App、Web)之间共享,而 Cookie 的跨域处理比较麻烦。
  • 可扩展性:更容易实现水平扩展。

对于传统的 Java Web 应用(如 JSP/Servlet),Session 仍然是一个非常成熟和简单的解决方案,但对于 RESTful API 或前后端分离项目,JWT 通常是更好的选择。

分享:
扫描分享到社交APP
上一篇
下一篇