深入理解 Cookie 与 Session —— Web 状态保持详解与实战
一、为什么需要 Cookie 和 Session?
HTTP 协议是**无状态(Stateless)**的,这意味着:
每次请求之间,服务器不会主动记住你是谁。
用户在一次请求中登录了,下一次请求服务器依然当你是新访客。
像购物车、个人中心、支付流程等功能无法在多次请求之间共享状态。
所以,状态保持技术的核心目标是:
让服务器在多个 HTTP 请求中“记住”客户端的身份和相关信息。
最常见的两种技术就是 Cookie 和 Session。
二、Cookie —— 浏览器端的状态记录
1. 概念
Cookie 是一小段存储在浏览器端的文本数据,由服务器生成并通过响应头 Set-Cookie
发送给浏览器保存。之后浏览器访问同一域名时会自动携带 Cookie 发送给服务器。
2. 工作原理
首次请求:浏览器访问服务器,服务器在响应头中设置 Cookie。
存储:浏览器将 Cookie 保存到本地文件(每个浏览器保存方式不同)。
后续请求:浏览器在请求头中自动带上 Cookie 信息。
服务器读取:服务器解析 Cookie,从而识别用户身份。
流程图示意:
[浏览器] --请求--> [服务器]
[服务器] --Set-Cookie--> [浏览器保存]
[浏览器] --携带Cookie请求--> [服务器读取并识别]
3. 示例
HTTP/1.1 200 OK
Set-Cookie: username=zhangsan; Path=/; HttpOnly; Max-Age=3600
下次请求:
GET /index.html HTTP/1.1
Cookie: username=zhangsan
4. Cookie 的关键属性
属性 | 作用 |
---|---|
Name=Value | 键值对数据 |
Domain | 指定 Cookie 生效的域名 |
Path | 指定 Cookie 生效的路径 |
Expires / Max-Age | 设置过期时间 |
HttpOnly | 防止 JavaScript 读取,增强安全性 |
Secure | 仅在 HTTPS 下传输 |
5. Cookie 的优缺点
优点:
浏览器自动管理,无需额外代码处理传输。
可以持久化存储(设置过期时间)。
缺点:
存储容量小(单个约 4KB)。
安全性差(数据明文存储在客户端,易被窃取或篡改)。
每次请求都会携带 Cookie,会增加流量消耗。
三、Session —— 服务器端的状态记录
1. 概念
Session 是一种服务器端存储的会话数据机制。
当用户第一次访问时,服务器会为其创建一个 Session,并分配一个唯一的 Session ID,将这个 ID 返回给浏览器保存(通常通过 Cookie)。
2. 工作原理
首次访问:服务器生成 Session 数据(例如登录状态、购物车内容),并生成唯一的 Session ID。
返回 Session ID:服务器通过 Cookie 将 Session ID 发给浏览器(如
JSESSIONID=xyz123
)。浏览器保存:浏览器保存 Session ID 到 Cookie。
后续请求:浏览器在请求中携带 Session ID,服务器根据该 ID 找到对应的 Session 数据。
3. 示例
Set-Cookie: JSESSIONID=xyz123; Path=/; HttpOnly
后续请求:
GET /cart HTTP/1.1
Cookie: JSESSIONID=xyz123
4. Session 特性
存储位置:服务器内存 / 数据库 / 分布式缓存(如 Redis)。
生命周期:默认 30 分钟(可配置)。
容量限制:取决于服务器内存,理论上可存储较大数据。
安全性:数据不直接暴露给客户端,安全性高。
四、Cookie 与 Session 的关系
实际上,Session 通常依赖 Cookie 来保存 Session ID:
Cookie 中只保存一个
Session ID
。具体的用户数据(登录状态、购物车等)存放在服务器的 Session 对象中。
这种方式结合了两者的优点:
减少 Cookie 存储压力。
提升安全性(敏感数据不放在客户端)。
五、Cookie 与 Session 的对比
对比项 | Cookie | Session |
---|---|---|
存储位置 | 客户端(浏览器) | 服务器端 |
安全性 | 易被窃取或篡改(需加密) | 高,数据不暴露给客户端 |
存储容量 | 单个 ≤ 4KB | 受服务器内存限制 |
生命周期 | 可设置过期时间 | 默认短时存储,可配置 |
性能影响 | 占用客户端空间,增加网络流量 | 占用服务器内存 |
使用场景 | 保存非敏感、需要持久化的数据 | 保存敏感数据(如登录信息) |
六、实际开发中的应用
1. 用户登录流程(Session 版)
用户输入账号密码并提交。
服务器验证成功后,创建 Session,保存用户信息。
将
Session ID
写入 Cookie。后续请求带上
Session ID
,服务器即可识别用户。
2. 用户登录流程(Cookie 版)
用户输入账号密码并提交。
服务器验证成功后,将用户信息加密后写入 Cookie。
后续请求直接解析 Cookie 获取用户信息(需注意安全风险)。
七、安全性问题与防护措施
Cookie 和 Session 都是状态保持机制,但如果使用不当,可能被攻击者利用。常见问题包括:
1. Cookie 劫持(Session Hijacking)
原理:攻击者获取了你的 Cookie(尤其是 Session ID),就能冒充你的身份。
场景:公共 WiFi 抓包、XSS 注入读取 Cookie。
防护措施:
使用 HTTPS 传输,防止中间人截获。
设置
HttpOnly
,防止 JS 脚本读取 Cookie。设置
Secure
,仅允许在 HTTPS 下传输。
2. Cookie 重定义(Cookie Overwriting)
原理:攻击者在客户端或通过响应头写入相同名称的 Cookie,从而覆盖原有 Cookie 内容,导致身份被冒用或数据被篡改。
场景:
同一域名下的不同子域篡改 Cookie。
恶意响应头设置同名 Cookie 覆盖原值。
防护措施:为 Cookie 设置明确的
Domain
和Path
限制作用范围。对 Cookie 值进行签名和验证,防止篡改。
3. Session 固定攻击(Session Fixation / Session 重定义)
原理:攻击者提前获取或制造一个合法的 Session ID,然后诱导受害者使用该 Session ID。一旦受害者登录,攻击者就能使用相同的 Session ID 登录到受害者的账号。
流程示例:
攻击者访问网站,获取一个合法的
Session ID=abc123
。攻击者将该 Session ID 发送给受害者(通过 URL、恶意链接等方式)。
受害者在该 Session ID 下登录。
攻击者用相同的 Session ID 就能直接登录受害者账号。
防护措施:
登录成功后重新生成 Session ID(关键)。
不允许通过 URL 传递 Session ID。
对 Session 设置较短的过期时间,并限制 IP/UA 绑定。
4. Session 过期处理不当
原理:Session 过期后,如果前端没有提示重新登录,可能导致用户操作失败,体验差。
防护措施:
后端返回特定状态码(如 401/440)。
前端检测到状态码后跳转到登录页,并可选保存用户输入内容。
八、Java 中的 Cookie 与 Session 实战
在 Java Web(Servlet)开发中,Cookie 和 Session 是内置支持的,我们可以很方便地使用它们。
1. Cookie 的使用
1.1 设置 Cookie
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.io.IOException;public class SetCookieServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {// 创建 Cookie 对象Cookie cookie = new Cookie("username", "zhangsan");// 设置 Cookie 过期时间(秒),这里设置 1 小时cookie.setMaxAge(60 * 60);// 设置作用路径(默认是当前项目路径)cookie.setPath("/");// 添加到响应response.addCookie(cookie);response.getWriter().write("Cookie 已设置!");}
}
1.2 读取 Cookie
public class GetCookieServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {Cookie[] cookies = request.getCookies();if (cookies != null) {for (Cookie c : cookies) {if ("username".equals(c.getName())) {response.getWriter().write("读取到 Cookie:username=" + c.getValue());}}} else {response.getWriter().write("没有找到 Cookie");}}
}
2. Session 的使用
2.1 创建与存储 Session 数据
public class SetSessionServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {// 获取 Session,没有则创建HttpSession session = request.getSession();// 存储数据到 Sessionsession.setAttribute("user", "zhangsan");// 设置 Session 过期时间(秒)session.setMaxInactiveInterval(30 * 60);response.getWriter().write("Session 已存储!");}
}
2.2 获取 Session 数据
public class GetSessionServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {HttpSession session = request.getSession(false); // false 表示没有就不创建if (session != null) {String user = (String) session.getAttribute("user");if (user != null) {response.getWriter().write("Session 中的 user = " + user);} else {response.getWriter().write("Session 中没有 user 数据");}} else {response.getWriter().write("Session 不存在");}}
}
3. 小技巧与注意事项
Session 默认使用 Cookie 存 Session ID(JSESSIONID),如果禁用 Cookie,可以通过 URL 重写:
response.encodeURL("myServlet");
Session 清除:
session.invalidate();
Cookie 安全性:对于敏感信息,应加密后再存储,或者仅存 Session ID。
Session 分布式存储:在分布式环境中使用 Redis 等中间件来存储 Session,实现会话共享。
九、总结
Cookie 适合存放少量非敏感数据,可跨会话持久化。
Session 安全性高,适合保存登录等敏感信息。
最佳实践:用 Cookie 存 Session ID,核心数据放 Session。
Java Web 开发 内置了 Cookie 和 Session API,用法简单高效。
在安全上,需防范 Cookie 重定义、Session 固定攻击、Cookie 劫持 等问题。