【Day34】Servlet 进阶:会话管理(Cookie vs Session)

本文收录于「Java 学习日记」专栏,聚焦 Java Web 会话管理核心,拆解 Cookie 与 Session 的底层原理、使用场景和实战技巧,帮你彻底搞懂 "登录状态怎么保持"~

一、为什么需要会话管理?

在上一篇 Servlet 的学习中,我们知道 HTTP 协议是无状态的 ------ 服务器不会记住每一次请求的客户端身份。比如:

  • 你第一次请求 "登录接口",输入账号密码登录成功;
  • 第二次请求 "个人中心接口",服务器根本不知道你是谁,会要求你重新登录。

这显然不符合实际需求,而会话管理(Cookie + Session) 就是解决这个问题的核心方案:

  • 会话(Session):服务器为每个客户端创建的 "身份标识",记录客户端的登录状态、个性化配置等;
  • Cookie:客户端(浏览器)存储的小型文本文件,用于携带会话标识,让服务器识别 "你是谁"。

简单来说:Cookie 是 "客户端身份证",Session 是 "服务器的身份档案",两者配合实现登录状态保持。

二、核心概念:Cookie 与 Session 的本质

1. Cookie:客户端的 "小本本"

Cookie 是服务器发送给客户端的小型文本数据,存储在浏览器中,核心特点:

  • 存储位置:客户端(浏览器),可分为 "内存 Cookie"(会话级,关闭浏览器失效)和 "硬盘 Cookie"(持久化,设置过期时间);
  • 大小限制:单个 Cookie 最大约 4KB,数量有限(一般每个域名最多 20 个);
  • 数据格式:键值对(key=value),可设置过期时间、作用路径、域名等;
  • 安全性:明文传输(HTTPS 可加密),不要存储密码、Token 等敏感信息。

2. Session:服务器的 "身份档案"

Session 是服务器为每个客户端创建的内存对象(也可持久化到 Redis / 数据库),核心特点:

  • 存储位置:服务器端,安全性更高;
  • 数据结构:键值对(可存储任意 Java 对象,无大小限制);
  • 关联方式:通过 Cookie 中的JSESSIONID(会话 ID)关联客户端和服务器的 Session;
  • 失效机制:默认超时失效(Tomcat 默认 30 分钟),也可手动销毁。
特性 Cookie Session
存储位置 客户端 服务器端
安全性 较低(明文) 较高(服务器内部)
数据大小 有限(4KB) 无限制(受服务器内存影响)
存储内容 简单字符串 任意 Java 对象
过期方式 可设置过期时间(持久化) 默认超时失效(Tomcat 30 分钟)
性能影响 减轻服务器压力 占用服务器内存,高并发需优化

三、实战 1:Cookie 的创建、读取与销毁

编写CookieServlet.java,向客户端发送 Cookie:

java

运行

java 复制代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/cookie/demo")
public class CookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 创建Cookie(键值对)
        Cookie usernameCookie = new Cookie("username", "Java日记");
        Cookie ageCookie = new Cookie("age", "18");
        
        // 2. 设置Cookie过期时间(可选,单位:秒)
        // 正数:持久化到硬盘,过期时间后失效;0:立即失效;负数:会话级Cookie(默认)
        usernameCookie.setMaxAge(24 * 60 * 60); // 1天过期
        ageCookie.setMaxAge(-1); // 会话级,关闭浏览器失效
        
        // 3. 设置Cookie作用路径(可选,默认当前项目路径)
        usernameCookie.setPath(request.getContextPath()); // /demo
        
        // 4. 发送Cookie到客户端(必须通过response添加)
        response.addCookie(usernameCookie);
        response.addCookie(ageCookie);
        
        // 响应提示
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("✅ Cookie已发送到客户端!");
    }
}

新增方法读取 Cookie:

java

运行

java 复制代码
@WebServlet("/cookie/read")
public class CookieReadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取客户端所有Cookie
        Cookie[] cookies = request.getCookies();
        
        // 2. 遍历Cookie(注意:cookies可能为null)
        response.setContentType("text/html;charset=UTF-8");
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                String value = cookie.getValue();
                response.getWriter().write("Cookie名称:" + name + ",值:" + value + "<br>");
            }
        } else {
            response.getWriter().write("❌ 客户端未携带Cookie!");
        }
    }
}

3. 销毁 Cookie(设置过期时间为 0)

java

运行

java 复制代码
@WebServlet("/cookie/delete")
public class CookieDeleteServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 销毁Cookie的核心:创建同名Cookie,设置maxAge=0,重新发送
        Cookie usernameCookie = new Cookie("username", "");
        usernameCookie.setMaxAge(0); // 立即失效
        usernameCookie.setPath(request.getContextPath()); // 路径必须和创建时一致
        response.addCookie(usernameCookie);
        
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("✅ Cookie已销毁!");
    }
}

4. 测试步骤

  1. 启动 Tomcat,访问 http://localhost:8080/demo/cookie/demo(发送 Cookie);
  2. 访问 http://localhost:8080/demo/cookie/read(读取 Cookie);
  3. 访问 http://localhost:8080/demo/cookie/delete(销毁 Cookie);
  4. 再次访问/cookie/read,确认username Cookie 已消失。

四、实战 2:Session 的创建、存取与销毁(登录状态保持)

1. 登录接口:创建 Session 并存储用户信息

java

运行

java 复制代码
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("/user/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 解决POST请求中文乱码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        
        // 2. 获取登录参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        
        // 3. 模拟登录校验(实际项目中查数据库)
        if ("admin".equals(username) && "123456".equals(password)) {
            // 4. 创建/获取Session(request.getSession():没有则创建,有则返回)
            HttpSession session = request.getSession();
            
            // 5. 存储用户信息到Session
            session.setAttribute("loginUser", username);
            session.setAttribute("userId", 1001);
            
            // 6. 设置Session超时时间(可选,单位:秒,优先级高于Tomcat默认配置)
            session.setMaxInactiveInterval(30 * 60); // 30分钟超时
            
            // 7. 获取SessionID(自动通过JSESSIONID Cookie发送到客户端)
            String sessionId = session.getId();
            response.getWriter().write("🎉 登录成功!SessionID:" + sessionId);
        } else {
            response.getWriter().write("❌ 用户名或密码错误!");
        }
    }
}

2. 个人中心接口:读取 Session 验证登录状态

java

运行

java 复制代码
@WebServlet("/user/center")
public class UserCenterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        
        // 1. 获取Session(false:没有则返回null,不创建新Session)
        HttpSession session = request.getSession(false);
        
        // 2. 验证Session是否存在,且用户已登录
        if (session != null && session.getAttribute("loginUser") != null) {
            String username = (String) session.getAttribute("loginUser");
            Integer userId = (Integer) session.getAttribute("userId");
            response.getWriter().write("👋 欢迎你," + username + "!用户ID:" + userId);
        } else {
            // 3. 未登录,重定向到登录页(或提示登录)
            response.sendRedirect("/demo/user/login.html");
        }
    }
}

3. 退出登录接口:销毁 Session

java

运行

java 复制代码
@WebServlet("/user/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(删除所有属性,并使Session失效)
        if (session != null) {
            session.invalidate();
        }
        
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("✅ 退出登录成功!");
    }
}

4. 测试准备:登录页面(login.html)

webapps/demo目录下创建login.html

html

预览

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
    <h1>用户登录</h1>
    <form action="/demo/user/login" method="post">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

5. 测试步骤

  1. 访问 http://localhost:8080/demo/login.html,输入admin/123456登录;
  2. 访问 http://localhost:8080/demo/user/center,能看到欢迎信息(登录状态保持);
  3. 访问 http://localhost:8080/demo/user/logout 退出登录;
  4. 再次访问/user/center,会重定向到登录页(登录状态失效)。
  • 当调用request.getSession()时,Tomcat 会自动创建JSESSIONID Cookie,发送到客户端;
  • 客户端后续请求会携带JSESSIONID,Tomcat 通过这个 ID 找到对应的 Session;
  • 若客户端禁用 Cookie,Session 会失效(可通过 URL 重写response.encodeURL()解决,进阶内容)。

修改UserCenterServlet,演示 URL 重写(兼容禁用 Cookie 的场景):

java

运行

java 复制代码
// 在/user/center接口中添加URL重写示例
String url = response.encodeURL("/demo/user/center");
response.getWriter().write("<br>URL重写后:" + url);

六、避坑指南:Cookie/Session 常见问题

  1. Cookie 中文乱码 :Cookie 不支持中文,存储前需编码(URLEncoder),读取后解码(URLDecoder):

    java

    运行

    java 复制代码
    // 存储中文Cookie
    String value = URLEncoder.encode("Java日记", "UTF-8");
    Cookie cookie = new Cookie("nickname", value);
    // 读取中文Cookie
    String value = URLDecoder.decode(cookie.getValue(), "UTF-8");
  2. Session 失效过快

    • 检查 Tomcat 默认超时配置(conf/web.xml<session-config>,默认 30 分钟);
    • 代码中通过session.setMaxInactiveInterval()手动设置超时时间;
  3. Session 共享问题:分布式项目中,单机 Session 无法共享,需用 Redis 存储 Session(进阶内容);

  4. Cookie 跨域问题 :Cookie 默认只在当前域名生效,跨域请求需配置SameSite=None; Secure(HTTPS 环境)。

七、今日实战小任务

  1. 编写 Servlet,向客户端发送包含中文的 Cookie(解决乱码),并读取验证;
  2. 实现完整的 "登录 - 保持登录状态 - 退出登录" 流程,验证 Session 的作用;
  3. 修改 Tomcat 的 Session 默认超时时间为 10 分钟,测试超时后 Session 是否失效。

总结

  1. 会话管理的核心是 Cookie(客户端存储)和 Session(服务器存储),HTTP 无状态特性通过两者配合实现登录状态保持;
  2. Cookie 是键值对文本,可设置过期时间,不要存储敏感信息;Session 是服务器对象,通过 JSESSIONID Cookie 关联客户端,安全性更高;
  3. 实战中,登录时将用户信息存入 Session,请求时读取 Session 验证登录状态,退出时销毁 Session,Cookie 需注意中文乱码和跨域问题。

下一篇【Day35】预告:JSP 核心详解(语法、内置对象、EL 表达式、实战案例),关注专栏持续解锁 Java Web 核心知识点~若本文对你有帮助,欢迎点赞 + 收藏 + 关注,你的支持是我更新的最大动力💖!

相关推荐
多米Domi0112 小时前
0x3f 第35天 电脑硬盘坏了 +二叉树直径,将有序数组转换为二叉搜索树
java·数据结构·python·算法·leetcode·链表
小+不通文墨2 小时前
“示波器的调节和使用”实验报告
经验分享·笔记·学习·学习方法
猫天意2 小时前
【深度学习小课堂】| torch | 升维打击还是原位拼接?深度解码 PyTorch 中 stack 与 cat 的几何奥义
开发语言·人工智能·pytorch·深度学习·神经网络·yolo·机器学习
zqmattack2 小时前
SQL优化与索引策略实战指南
java·数据库·sql
crossaspeed2 小时前
Java-线程池(八股)
java·开发语言
带刺的坐椅2 小时前
又一个项级的 Java Multi Agent 开源项目
java·ai·agent·solon·智能体
Voyager_43 小时前
StringRedisTemplate 和 RedisTemplate 的区别是什么?
java·spring boot
杏花春雨江南3 小时前
JavaWeb企业级项目实战:从SSH到SSM演进 + MQ/Redis/ES高可用架构落地全复盘(实战干货+避坑指南)
java·jvm·spring
UR的出不克3 小时前
使用 Python 爬取 Bilibili 弹幕数据并导出 Excel
java·python·excel