Cookie和Session
Cookie和Session
完整流程
# 首次请求(无Cookie)
GET /login HTTP/1.1
Host: example.com
# 服务器响应(设置Cookie)
HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=28E01391C799D5852AD3F062E9FAC1A2; Path=/; HttpOnly
# 后续请求(浏览器自动添加)
GET /dashboard HTTP/1.1
Host: example.com
Cookie: JSESSIONID=28E01391C799D5852AD3F062E9FAC1A2
-
首次请求(无Cookie):
- 浏览器第一次访问服务器(如 Java Web 应用)
- 请求头中没有
Cookie字段
-
服务器响应(设置Cookie):
-
服务器创建会话 → 生成唯一
JSESSIONID(如28E01391C799D5852AD3F062E9FAC1A2) -
在响应头中添加:
Set-Cookie: JSESSIONID=28E01391C799D5852AD3F062E9FAC1A2; Path=/; HttpOnly
-
-
浏览器保存:
- 浏览器收到后,将
JSESSIONID保存到本地(内存/磁盘)
- 浏览器收到后,将
-
后续请求(自动携带):
-
浏览器自动在所有符合条件(同域、Path匹配)的请求头中添加:
Cookie: JSESSIONID=28E01391C799D5852AD3F062E9FAC1A2
-
| 角色 | 职责 |
|---|---|
| 服务器 | 生成 JSESSIONID,通过 Set-Cookie 响应头下发 |
| 浏览器 | 1. 存储收到的 Cookie 2. 自动附加到后续请求头中 3. 管理过期、作用域等 |
- 会话型 vs 持久型:
- 无
Expires/Max-Age→ 浏览器关闭后删除(会话 Cookie) - 有有效期 → 持久保存到过期
- 无
对比表格:服务器会话 vs 浏览器Cookie
| 方面 | 服务器会话(Session) | 浏览器Cookie(JSESSIONID) |
|---|---|---|
| 存储位置 | 服务器内存/Redis/数据库 | 浏览器内存/磁盘 |
| 失效触发方 | 服务器主动控制 | 浏览器根据规则自动处理 |
| 两者关系 | 会话是本体,Cookie只是钥匙 | 钥匙,用于查找服务器上的会话 |
Session(服务器会话)解析
服务器会话Session什么时候创建,什么时候销毁,它有什么功能?
Session 的核心功能
Session 的核心功能是在服务器端维持用户状态,让 HTTP 这种无状态协议能够“记住”用户。主要功能包括:
- 用户身份认证 - 存储登录状态
- 购物车数据 - 电商网站常用
- 表单多页数据暂存 - 跨页面保持数据
- 用户偏好设置 - 主题、语言等
- 临时数据处理 - 验证码、临时令牌
Session 的创建时机
1.首次请求时自动创建(最常见)
// Java Servlet 中,以下情况会创建 Session:
HttpSession session = request.getSession(); // 如果不存在则创建
HttpSession session = request.getSession(true); // 明确要求创建
触发条件:
- 第一次访问网站
- 调用了
request.getSession()或request.getSession(true)- 请求带Cookie: JSESSIONID=4F58149128D0BCB2 → Tomcat内存查找 → 找到则返回,找不到则新建
- 如果新建的话,服务器自动创建并生成唯一 Session ID
- 通过
Set-Cookie响应头将 JSESSIONID 发给浏览器
Session 的销毁时机
- 超时销毁(默认方式)
<!-- web.xml 配置 -->
<session-config>
<session-timeout>30</session-timeout> <!-- 30分钟无活动 -->
</session-config>
计时规则:
- 从最后一次访问时间开始计算
- 每次
request.getSession()都会刷新时间 - 服务器有定时任务清理过期 Session
- 主动销毁(程序控制)
// Java - 退出登录
session.invalidate(); // 立即销毁,抛出异常
// 安全退出建议写法
try {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
} catch (IllegalStateException e) {
// 会话已无效
}
- 服务器重启
- 内存中的 Session 数据丢失(除非持久化)
- 需要配合外部存储(Redis)避免此问题
- 服务器强制清理
# Tomcat 配置 context.xml
<Manager className="org.apache.catalina.session.PersistentManager"
maxActiveSessions="1000" # 最大会话数
minIdleSwap="300" # 空闲300秒可交换到磁盘
maxIdleBackup="600"/> # 空闲600秒备份
Session 生命周期示例
// 完整流程示例
public class SessionLifecycle {
// 1. 用户首次访问(创建)
public void firstVisit(HttpServletRequest request) {
HttpSession session = request.getSession(); // 创建新Session
session.setAttribute("visitCount", 1);
// 服务器:创建Session对象,生成JSESSIONID
// 浏览器:收到 Set-Cookie: JSESSIONID=ABC123
}
// 2. 用户后续操作(保持)
public void addToCart(HttpServletRequest request, Product product) {
HttpSession session = request.getSession(); // 获取现有Session
List<Product> cart = (List<Product>) session.getAttribute("cart");
cart.add(product);
// 刷新最后访问时间(30分钟倒计时重置)
}
// 3. 用户30分钟无操作(超时)
// 服务器定时任务检测到 lastAccessedTime + 30分钟 < 当前时间
// → 销毁Session对象,释放内存
// 4. 用户退出登录(主动销毁)
public void logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.removeAttribute("user"); // 移除单个属性
session.invalidate(); // 彻底销毁
// 可选:让浏览器删除Cookie
Cookie cookie = new Cookie("JSESSIONID", "");
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
}
请求的时候,不去getSession()还会创建吗?
只有主动调用 request.getSession() 或 request.getSession(true) 时才会创建 Session。
- 默认情况:不创建
// 示例1:不创建Session
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// 只是处理请求,没有调用getSession()
// 服务器不会自动创建Session!
String param = request.getParameter("id"); // OK
// 到此处,请求处理完毕,没有Session被创建
}
- 显式获取(可能创建)
// getSession() - 默认创建
HttpSession session = request.getSession();
// ↑ 等价于 request.getSession(true)
// 如果Session不存在,则创建新的
// getSession(false) - 不创建
HttpSession session = request.getSession(false);
// ↑ 如果Session不存在,返回null,不创建
- 框架行为差异
// Spring Security 可能在认证时自动创建
// 但这不是Servlet容器的默认行为
// 某些框架的拦截器/过滤器可能意外创建Session
// 需要检查框架配置
不必要Session创建的问题:
- 内存浪费:每个Session占用几KB到几MB
- GC压力:大量短期Session增加垃圾回收频率
- 存储开销:集群环境下同步Session的网络开销
- 安全风险:更多Session对象增加攻击面
tomcat控制和管理session
Tomcat:实际创建、存储、管理Session对象
创建出来的session都是tomcat那边控制和管理,我的doGet方法里,只是去获取使用
你的代码(doGet方法) -> 获取/使用Session引用 -> Tomcat管理实际Session对象
// 你的Servlet代码(表面)
public class MyServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
// 1. 获取Session(问Tomcat要)
HttpSession session = request.getSession();
// 2. 使用Session(操作Tomcat管理的对象)
Integer count = (Integer) session.getAttribute("count");
if (count == null) count = 0;
session.setAttribute("count", ++count);
// 3. 输出(只是读数据)
response.getWriter().write("访问次数: " + count);
}
}
// Tomcat内部(实际发生)
class RequestFacade implements HttpServletRequest {
public HttpSession getSession() {
// 委托给Tomcat的Session管理器
return context.getManager().getSession(this, true);
}
}
// Tomcat的Session管理器
class StandardManager {
public Session getSession(Request request, boolean create) {
String requestedSessionId = request.getRequestedSessionId();
if (requestedSessionId != null) {
// 1. 查找现有Session
Session session = sessions.get(requestedSessionId);
if (session != null && !session.isExpired()) {
session.access(); // 更新最后访问时间
return session;
}
}
if (create) {
// 2. 创建新Session
Session session = createSession();
// 3. 设置Cookie(通过Response)
Cookie cookie = new Cookie("JSESSIONID", session.getId());
response.addCookie(cookie);
return session;
}
return null;
}
}
Cookie解析
Cookie是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储这些数据,并在后续请求中自动携带它们发送回服务器。
Cookie的生命周期
1. 创建时机
// 服务器端创建Cookie
Cookie cookie = new Cookie("username", "张三");
cookie.setMaxAge(60 * 60 * 24); // 1天
response.addCookie(cookie); // 通过响应头发送给浏览器
// tomcat + HttpServlet里面
HttpSession session = req.getSession();
// 只要HttpServletRequest执行getSession()方法,回包就会加上Cookie。(tomcat底层代码处理的)
创建场景:
- 用户登录成功时(存储登录令牌)
- 保存用户偏好设置(语言、主题等)
- 记录用户行为(购物车、浏览历史)
- 实现"记住我"功能
2. 销毁时机
// 方式1:设置过期时间为过去
Cookie cookie = new Cookie("username", "");
cookie.setMaxAge(0); // 立即过期
response.addCookie(cookie);
// 方式2:浏览器行为
// - 到达Expires/Max-Age时间
// - 用户手动清除浏览器数据
// - 隐私模式关闭时
📊 Cookie的类型对比
| 类型 | 创建方式 | 生命周期 | 存储位置 | 示例 |
|---|---|---|---|---|
| 会话Cookie | 不设置Max-Age/Expires |
浏览器关闭时 | 浏览器内存 | JSESSIONID |
| 持久Cookie | 设置Max-Age或Expires |
到达过期时间 | 浏览器磁盘文件 | 记住登录状态 |
| 安全Cookie | setSecure(true) |
同上 | 同上 | HTTPS专用 |
| HttpOnly | setHttpOnly(true) |
同上 | 同上 | 防止XSS攻击 |
Cookie的存储位置
1. 浏览器中的位置
不同浏览器的存储位置:
├── **Chrome**
│ ├── Windows: C:\Users\[用户名]\AppData\Local\Google\Chrome\User Data\Default\Cookies
│ ├── macOS: ~/Library/Application Support/Google/Chrome/Default/Cookies
│ └── Linux: ~/.config/google-chrome/Default/Cookies
├── **Firefox**
│ ├── Windows: C:\Users\[用户名]\AppData\Roaming\Mozilla\Firefox\Profiles\xxx.default\cookies.sqlite
│ └── macOS/Linux: ~/.mozilla/firefox/xxx.default/cookies.sqlite
├── **Edge**: 类似Chrome路径
└── **Safari**
└── macOS: ~/Library/Cookies/Cookies.binarycookies
2. 存储格式
-- 以SQLite数据库为例(Chrome/Firefox)
CREATE TABLE cookies (
creation_utc INTEGER, -- 创建时间
host_key TEXT, -- 域名
name TEXT, -- Cookie名称
value TEXT, -- Cookie值
path TEXT, -- 路径
expires_utc INTEGER, -- 过期时间
is_secure INTEGER, -- 是否仅HTTPS
is_httponly INTEGER, -- 是否HttpOnly
last_access_utc INTEGER, -- 最后访问时间
has_expires INTEGER, -- 是否有过期时间
is_persistent INTEGER -- 是否持久化
);
- 内存 vs 磁盘存储
// 会话Cookie:存储在浏览器内存中
Cookie sessionCookie = new Cookie("session", "abc123");
// 不设置MaxAge → 浏览器内存存储
// 持久Cookie:存储在磁盘文件中
Cookie persistentCookie = new Cookie("preference", "dark");
persistentCookie.setMaxAge(30 * 24 * 60 * 60); // 30天
// 设置MaxAge → 浏览器磁盘文件存储
总结
浏览器只维护cookie,请求的时候把cookkie再传递给服务器是吗?浏览器会读取cookie里面的信息吗
- ✅ 浏览器只维护Cookie:存储、管理和清理
- ✅ 请求时自动传递:符合条件时自动附加到请求头
- ✅ 浏览器会读取Cookie吗?:会,但是一般不用这个传递信息的。
流程图:
服务器发送响应
↓
Set-Cookie: name=value; HttpOnly
↓
浏览器接收并存储Cookie
├── 内存(会话Cookie)
└── 磁盘文件(持久Cookie)
↓
用户发起新请求
↓
浏览器自动检查:
├── 域名匹配?
├── 路径匹配?
├── 未过期?
├── Secure要求满足?
└── SameSite策略允许?
↓
符合条件的Cookie自动添加到请求头
↓
Cookie: name1=value1; name2=value2
↓
发送给服务器
常见的数据类型:
// 1. 用户身份信息
new Cookie("user_id", "12345");
new Cookie("username", "wdy");
new Cookie("email", "wdy@example.com");
// 2. 会话标识
new Cookie("JSESSIONID", "28E01391C799D5852AD3F062E9FAC1A2");
new Cookie("PHPSESSID", "abc123def456");
// 3. 用户偏好
new Cookie("theme", "dark");
new Cookie("language", "zh-CN");
new Cookie("timezone", "Asia/Shanghai");
// 4. 跟踪/分析数据
new Cookie("_ga", "GA1.2.1234567890.1234567890");
new Cookie("_gid", "GA1.2.9876543210.9876543210");
// 5. 购物车/临时数据
new Cookie("cart_id", "cart_789");
new Cookie("last_viewed", "product_123");
// 6. 认证令牌
new Cookie("auth_token", "eyJhbGciOiJIUzI1NiIs...");
new Cookie("remember_me", "true");
这些发给浏览器,浏览器不会用的吧,它只是保存,然后下次请求的时候,它再传给服务器,服务器从cookie里面拿想要的信息,进行设置,比如拿语言选择,设置语言?
解答:是这样的,浏览器只是保存。
第一次请求(无Cookie)
↓
服务器响应(设置Cookie)
↓ Set-Cookie: language=zh-CN; theme=dark
浏览器保存Cookie
↓ (只存不用)
用户第二次请求
↓
浏览器自动附加Cookie
↓ Cookie: language=zh-CN; theme=dark
服务器接收并解析Cookie
↓
服务器使用Cookie数据
├── 看到language=zh-CN → 返回中文页面
├── 看到theme=dark → 应用暗黑主题
├── 看到user_id=123 → 加载用户数据
└── 看到JSESSIONID=xxx → 恢复会话
问题:意思就是http是短连接,每次请求就要去校验账号密码的话很麻烦,有了cookie,浏览器请求每次自带cookie?????
完全正确!您已经理解了Cookie最根本的价值!
HTTP是"健忘症患者",Cookie是它的"记忆助手"!
没有Cookie的"地狱模式"
场景:每次请求都要验证身份
# ❌ 地狱版HTTP:每次请求都带账号密码
GET /home HTTP/1.1
Authorization: Basic d2R5OjEyMzQ1Ng== # base64编码的"wdy:123456"
GET /profile HTTP/1.1
Authorization: Basic d2R5OjEyMzQ1Ng==
GET /cart HTTP/1.1
Authorization: Basic d2R5OjEyMzQ1Ng==
# 问题:
# 1. 密码在网络中传输N次(安全隐患)
# 2. 服务器每次都要查数据库验证(性能差)
# 3. 用户每次操作都要输密码(体验差)
有Cookie的"天堂模式"
场景:一次登录,处处通行
# ✅ 第一次登录(输入密码)
POST /login HTTP/1.1
Content-Type: application/json
{"username": "wdy", "password": "123456"}
# 服务器响应:给你一个"通行证"
HTTP/1.1 200 OK
Set-Cookie: session_token=abc123def456; HttpOnly; Secure
# ✅ 后续所有请求(自动带通行证)
GET /home HTTP/1.1
Cookie: session_token=abc123def456 # ← 浏览器自动添加!
GET /profile HTTP/1.1
Cookie: session_token=abc123def456 # ← 自动添加!
GET /cart HTTP/1.1
Cookie: session_token=abc123def456 # ← 自动添加!
Cookie过期 或 Session过期
第一次请求登录后服务器创建session,并且会返回cookie到浏览器,cookie记录登录信息,后面每次浏览器请求就会自动携带cookie,这样服务器就可以根据cookie拿到已经登录的信息。如果cookie过期了,浏览器就得重新登录,如果服务器的session过期了也得浏览器重新登录。
用户第一次登录
↓
服务器验证账号密码 ✅
↓
创建Session(存服务器) + 设置Cookie(JSESSIONID=xxx)
↓
浏览器保存Cookie
↓
后续所有请求自动携带Cookie
↓
服务器用JSESSIONID查找Session → 获取登录信息
↓
会话保持中...
↓
❌ Cookie过期 或 ❌ Session过期
↓
服务器找不到有效Session
↓
返回"需要重新登录"
↓
用户重新登录(重新开始循环)
两种过期情况的详细对比
情况1:Cookie过期(钥匙丢了)
// 浏览器端:Cookie过期了
Cookie: JSESSIONID=abc123 // ⏰ 这个Cookie已经过期了!
// 服务器收到的请求:
GET /profile HTTP/1.1
Cookie: // ⚠️ 空的!或者没有JSESSIONID
// 服务器处理:
HttpSession session = request.getSession(false); // 返回null
if (session == null) {
// ❌ 需要重新登录
response.sendRedirect("/login");
}
情况2:Session过期(保险箱清空了)
// 浏览器端:Cookie还在
Cookie: JSESSIONID=abc123 // ✅ 钥匙还在
// 服务器收到的请求:
GET /profile HTTP/1.1
Cookie: JSESSIONID=abc123 // ✅ 带上了钥匙
// 服务器处理:
HttpSession session = request.getSession(false);
// 但是!Session abc123在服务器上已经过期被清理了!
// session = null
if (session == null) {
// ❌ 钥匙还在,但保险箱没了
// 需要重新登录
response.sendRedirect("/login");
}
过期时间管理策略
1. Cookie过期时间设置
// 场景1:会话Cookie(浏览器关闭就过期)
Cookie sessionCookie = new Cookie("JSESSIONID", sessionId);
// 不设置setMaxAge → 浏览器关闭时删除
// 场景2:持久Cookie(固定时间过期)
Cookie persistentCookie = new Cookie("JSESSIONID", sessionId);
persistentCookie.setMaxAge(7 * 24 * 60 * 60); // 7天
// 场景3:记住我功能(长时间有效)
Cookie rememberMeCookie = new Cookie("remember_token", token);
rememberMeCookie.setMaxAge(30 * 24 * 60 * 60); // 30天
2. Session过期时间设置
<!-- Tomcat配置:web.xml -->
<session-config>
<!-- 默认30分钟无活动就过期 -->
<session-timeout>30</session-timeout>
</session-config>
// 程序控制
HttpSession session = request.getSession();
session.setMaxInactiveInterval(60 * 60); // 1小时
HttpSession
工作原理
浏览器首次访问
↓
服务器创建 HttpSession 对象
↓ 生成唯一 Session ID (如: 28E01391C799D5852AD3F062E9FAC1A2)
↓ 存储到服务器内存/Redis
↓ 通过 Set-Cookie 响应头发送 JSESSIONID 给浏览器
↓
浏览器保存 JSESSIONID Cookie
↓
后续请求自动携带 JSESSIONID
↓
服务器通过 JSESSIONID 找到对应的 HttpSession
↓ 获取/设置 Session 属性
↓ 返回响应
tomcat启动后,获取 HttpSession 对象
// 方式1:获取现有Session,如果没有则创建新Session
HttpSession session = request.getSession(); // 相当于 getSession(true)
// 方式2:获取现有Session,如果没有则返回null
HttpSession session = request.getSession(false); // 不自动创建
// 方式3:检查请求中是否包含有效的Session ID
String requestedSessionId = request.getRequestedSessionId();
boolean isValid = request.isRequestedSessionIdValid();
// 设置属性(存储数据)
HttpSession session = req.getSession();
session.setAttribute("user", "张三");
session.setAttribute("count", 100);
Session 存储机制
1. 默认存储(内存)
// Tomcat 内部实现(简化版)
public class StandardManager implements Manager {
// 使用ConcurrentHashMap存储所有Session
private Map<String, Session> sessions = new ConcurrentHashMap<>();
// 创建新Session
public Session createSession() {
String sessionId = generateSessionId();
StandardSession session = new StandardSession(this);
session.setId(sessionId);
session.setCreationTime(System.currentTimeMillis());
sessions.put(sessionId, session);
return session;
}
// 后台线程清理过期Session
public void backgroundProcess() {
for (Session session : sessions.values()) {
if (session.isExpired()) {
session.expire();
sessions.remove(session.getId());
}
}
}
}
2. 分布式 Session 存储
<!-- 使用 Redis 存储 Session -->
<Context>
<Manager className="org.apache.catalina.session.PersistentManager"
saveOnRestart="true"
maxActiveSessions="10000">
<Store className="org.apache.catalina.session.RedisStore"
host="redis-cluster.example.com"
port="6379"
password="${redis.password}"
database="0"
timeout="2000"
sentinelMaster="mymaster"
sentinels="sentinel1:26379,sentinel2:26379"/>
</Manager>
</Context>
保存的一个实例
@WebServlet("/test")
public class EncodingTest extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
HttpSession session = req.getSession();
System.out.println("session id: " + session.getId());
System.out.println("session : " + session);
System.out.println("session : " + session.getAttribute("user") + " " + session.getAttribute("count"));
session.setAttribute("user", "张三");
session.setAttribute("count", 100);
}
}

问题:社区版IDEA,因为没有tomcat的选项,只能安装Smart Tomcat插件。然后用这个启动,请求一次/test,会创建session和设置属性。
重启tomcat,在执行请求一次/test,发现直接能拿到上次设置的属性?这是为什么?哪里持久化了HttpSession吗?
解答:
Smart Tomcat自动持久化了Session → 重启后恢复。持久化文件存放的地方不知道,搜了都搜不到。
浙公网安备 33010602011771号