HTTP 的状态保存方案
HTTP 无状态之上的状态保存:Cookie/Session/Token(JWT)/Web Storage 的定位与取舍。
#type / synthesis
#status / evergreen
#resource / http
#tech / network
#platform / browser
[!info] related notes
- 所属 MOC: http-and-frontend-networking-moc
- HTTP 语义: http
- Cookie/Session/Token: cookie-session-token, jwt
- CSRF: csrf
HTTP 的状态保存方案
HTTP 协议天生是“失忆”的(无状态,Stateless) 你对服务器发出一万次请求,在服务器眼里,你每次都是一个完全陌生的人。为了让服务器“记住”你(比如记住你登录了、记住你的购物车里有什么),业界演进出了几种主流的状态保存方案。
总的来说,可以分为客户端保存、服务端保存以及现代混合方案三大类。
1. 传统双剑客:Cookie 与 Session
这是 Web 早期最经典、至今仍被广泛使用的状态保存方案。它们通常是配合使用的。
A. Cookie(客户端的“备忘录”)
Cookie 是 HTTP 协议内置的机制。服务器通过响应头(Set-Cookie)强行往你的浏览器里塞一张“小纸条”,以后浏览器每次请求这个服务器,都会自动带上这张“小纸条”(Cookie 请求头)。
- 优点: 浏览器原生支持,全自动携带(也就是我们上一题讨论的 CSRF 的万恶之源)。
- 缺点: 容量极小(通常只有 4KB),极度不安全(如果明文存敏感信息,很容易被窃取或篡改),而且每次请求都带,浪费带宽。
B. Session(服务端的“档案柜”)
因为 Cookie 存不了太多东西也不安全,聪明的人类发明了 Session。
- 工作原理: 你登录后,服务器在自己的内存(或 Redis 数据库)开辟一个专属的“档案袋”,里面存着你的用户资料、购物车等状态。然后,服务器生成一个唯一的档案号(Session ID)。
- 它俩如何配合: 服务器把这个
Session ID放在 Cookie 里发给浏览器。下次你再来,浏览器自动交出带有Session ID的 Cookie,服务器拿着 ID 去档案柜里一查,就知道你是谁了。 - 优点: 极其安全。敏感数据全在服务器端,黑客只能拿到一串无意义的 Session ID。
- 缺点: 极度消耗服务器内存。如果有一千万用户同时在线,服务器就要维护一千万个档案袋。而且在分布式架构下(多台服务器),很难保证你的 Session ID 请求每次都落在同一台服务器上(这叫 Session 共享难题)。
2. 现代微服务架构的首选:Token / JWT
随着移动端 App 的崛起和前后端分离架构(Vue/React)的普及,服务器不想再维护沉重的 Session 档案柜了,于是 Token 方案应运而生。
- 工作原理: 这就是我们最开始讨论的方案。服务器不再保存你的状态。你登录后,服务器把你的身份信息(User ID、角色等)用加密算法签个名,做成一个 JWT (JSON Web Token),直接发给你。
- 状态在哪里? 状态全部保存在 Token 本身里。 每次请求,客户端把 Token 发过来,服务器只需要用密钥解密验证一下签名:“嗯,确实是我签发的,而且没被篡改过”,就能知道你是谁。
- 优点: 彻底解放了服务器内存(无状态化),天生支持跨域,极其适合多端(Web、App、小程序)和微服务/Serverless 架构。
- 缺点: Token 一旦签发,在过期前很难主动作废(除非服务器强行维护一个黑名单,但这又回到了 Session 的老路);如果 Token 包含数据过多,体积会变大,增加网络开销。
3. HTML5 时代的客户端仓库:Web Storage
如果我们只是想在前端保存一些不需要发给服务器的状态(比如页面的夜间模式开关、表单填写到一半的草稿),用 Cookie 显然不合适(因为会增加 HTTP 请求体积)。
localStorage: 持久化存储(一般 5MB 左右)。除非用户手动清空或代码删除,否则数据永远在。这就是我们第一题提到的存 JWT 的地方之一。sessionStorage: 会话级存储。只要你关掉当前浏览器标签页,数据立马销毁。IndexedDB: 浏览器里的本地数据库,可以存几十上百 MB 的结构化数据,适合做离线 Web 应用。
4. 古老且少用的“偏方”(了解即可)
- URL 重写: 把状态直接拼在 URL 后面,比如
http://example.com/page?session_id=123。这通常是在用户浏览器彻底禁用了 Cookie 时的极端备用方案,极度不安全(别人发个链接就能劫持你的状态)。 - 隐藏表单字段: 在 HTML 表单里放一个
<input type="hidden" name="session_id" value="123">。只有提交表单时才会带上状态,局限性极大。
📊 核心状态保存方案对比表
| 方案 | 状态存储位置 | 是否依赖 HTTP 自动机制 | 适用场景 | 核心痛点 |
|---|---|---|---|---|
| Cookie | 客户端(浏览器) | ✅ 是(自动带入 Header) | 存储少量非敏感标识、追踪用户行为 | 容量太小、易引发 CSRF |
| Session | 服务端(内存/Redis) | ✅ 依赖 Cookie 传 ID | 传统单体架构、对安全性要求极高的系统 | 占用服务端资源、分布式扩展难 |
| Token (JWT) | 客户端(Storage 或 Cookie) | ❌ 取决于存哪(通常手动放 Header) | 前后端分离、微服务、多端共用 API | 难以主动注销/吊销 |