你是否好奇过:为什么网站能记住你的登录状态?为什么购物车里的商品换个页面还在?这背后离不开两个核心角色——Cookie 和 Session。它们共同撑起了 Web 应用的状态管理,让无状态的 HTTP 协议拥有了“记忆”。今天,我们不仅会讲清楚它们的原理,还会聊聊安全、性能以及现代 Web 中的替代方案。

1. 为什么需要 Cookie 和 Session?

HTTP 协议本身是无状态的。这意味着每个请求都是独立的,服务器收到一个请求时,并不天然知道这个请求和之前的请求是否来自同一个用户。

想象一下:你登录了网银,查询余额时服务器却问“你是谁”;把商品加入购物车,结算时购物车却空空如也——这显然不可接受。

为了让服务器能“认出”同一个用户,Cookie 和 Session 应运而生。

2. Cookie:客户端的身份凭证

Cookie 是服务器发送给浏览器的一小段文本数据(通常不超过 4KB)。浏览器会将其存储下来,并在后续的每次请求中,自动将同名的 Cookie 带回到服务器。

2.1 Cookie 的典型工作流程

  1. 用户首次访问网站,浏览器发起请求。

  2. 服务器在响应头中添加 Set-Cookie 指令,例如:

    text

    Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
  3. 浏览器收到后,解析并保存这个 Cookie。

  4. 之后对同一域名的每次请求,浏览器都会自动在请求头中加入:

    text

    Cookie: sessionId=abc123
  5. 服务器读取这个 Cookie,识别用户身份。

2.2 Cookie 的关键属性

属性 作用 重要性
Name=Value 核心数据,键值对形式 必选
Domain / Path 控制 Cookie 的生效范围 安全隔离
Expires / Max-Age 持久化 Cookie 的有效期 会话 Cookie 若不设置,浏览器关闭即失效
HttpOnly 禁止 JavaScript 访问,有效防御 XSS 攻击 ⭐ 安全必设
Secure 仅通过 HTTPS 传输 ⭐ 安全必设
SameSite 控制跨站请求是否携带 Cookie,防御 CSRF ⭐ 重要(Lax 或 Strict)

2.3 Cookie 的两类形态

  • 会话级 Cookie:不设置 Expires 或 Max-Age,存储在内存中,浏览器关闭即消失。

  • 持久化 Cookie:设置了过期时间,存储在硬盘上,到期前一直有效(即使用户重启电脑)。

3. Session:服务端的私密档案夹

如果说 Cookie 是把“身份牌”发给用户保管,那么 Session 就是服务器端的一本花名册。Session 数据存储在服务器上,客户端只保存一个唯一标识 —— Session ID(通常通过 Cookie 传递)。

3.1 Session 的工作流程

  1. 用户首次访问,服务器创建一个 Session 对象,生成唯一的 Session ID。

  2. 服务器通过 Set-Cookie 将 Session ID 发送给浏览器(通常命名为 JSESSIONIDsid 等)。

  3. 浏览器后续请求自动带上这个 Session ID

  4. 服务器根据 Session ID 查找对应的 Session 数据,从而获取用户的登录信息、购物车等状态。

3.2 Session 的存储方式

Session 数据可以存放在:

  • 内存(默认开发场景,生产环境不要用,重启丢失且无法共享)

  • 数据库(MySQL、PostgreSQL)

  • 缓存(Redis、Memcached)—— 生产环境首选

3.3 为什么 Session 比 Cookie 更安全?

敏感数据不经过客户端。如果直接把用户 ID、权限等级存在 Cookie 里,任何人都可以篡改(例如把 role=user 改成 role=admin)。而 Session 只把不可猜测的随机 ID 给客户端,真实数据躺在受服务器保护的存储中,无法被用户直接篡改。

4. 一张表看懂 Cookie 与 Session 的核心区别

维度 Cookie Session
存储位置 浏览器端 服务器端
容量限制 约 4KB 无硬性限制(受服务器内存/存储限制)
安全性 较低(数据可见,可篡改,需加密签名) 较高(ID 不可猜测,真实数据不暴露)
性能 无服务器存储压力 大量 Session 会占用服务器资源
跨域支持 需特殊配置,默认同源 依赖 Session ID 的传递方式
移动端适用性 依赖 Cookie 机制,部分 App 需手动处理 可用 Token 替代

5. 实战:Node.js + Express 演示登录与状态保持

下面我们用一个极简的例子来串联整个过程。

javascript

// 安装依赖:npm install express express-session cookie-parser
const express = require('express');
const session = require('express-session');
const app = express();

// 配置 Session 中间件
app.use(session({
  secret: 'your-secret-key',   // 用于签名 Session ID
  resave: false,
  saveUninitialized: true,
  cookie: { 
    httpOnly: true,
    secure: false,   // 生产环境必须 true (HTTPS)
    maxAge: 1000 * 60 * 30   // 30 分钟
  }
}));

// 登录接口(模拟)
app.post('/login', (req, res) => {
  // 假设验证用户名密码成功
  req.session.user = { id: 1, name: 'Alice' };
  req.session.loggedIn = true;
  res.send('登录成功,Session 已建立');
});

// 需要登录的接口
app.get('/profile', (req, res) => {
  if (req.session.loggedIn) {
    res.json(req.session.user);
  } else {
    res.status(401).send('请先登录');
  }
});

// 退出登录
app.post('/logout', (req, res) => {
  req.session.destroy(err => {
    if (err) return res.status(500).send('退出失败');
    res.clearCookie('connect.sid'); // 清除 Session Cookie
    res.send('已退出');
  });
});

app.listen(3000);

验证步骤

  1. 使用 curl 或浏览器先调用 /login,注意响应头中的 Set-Cookie

  2. 调用 /profile 时带上 Cookie 头,就能获取用户信息。

  3. 调用 /logout 后,再访问 /profile 会返回 401。

6. 安全隐患与最佳防御

Cookie + Session 虽然经典,但并非银弹。以下是三大典型攻击及对策。

6.1 XSS(跨站脚本攻击)

攻击者注入恶意脚本,读取 document.cookie 盗取 Session ID。

防御

  • 设置 HttpOnly,禁止 JS 读取 Cookie。

  • 对用户输入进行严格的过滤/转义。

  • 使用 CSP(内容安全策略)。

6.2 CSRF(跨站请求伪造)

攻击者诱导用户点击恶意链接,利用浏览器自动携带 Cookie 的机制,以用户身份发送伪造请求。

防御

  • 关键操作使用 SameSite=Lax 或 Strict

  • 使用 CSRF Token(服务端下发随机 token,提交时验证)。

  • 验证 Referer / Origin 头。

6.3 Session 劫持

攻击者通过网络嗅探或 XSS 窃取 Session ID。

防御

  • 强制 HTTPS + Secure 标记。

  • 定期轮换 Session ID(登录后、权限变更后调用 req.session.regenerate())。

  • 绑定客户端特征(如 IP 段、User-Agent 哈希)。

7. 现代 Web 的演进:Token 与 JWT

分布式微服务和移动端普及后,传统 Session 暴露出一些短板:

  • 服务器必须集中存储 Session(Redis 可解,但增加架构复杂度)。

  • 无法轻松跨域或跨服务共享。

  • 移动端 App 处理 Cookie 不如浏览器那么自动。

于是 Token(令牌) 方案流行起来,尤其是 JWT(JSON Web Token)

JWT 的特点

  • 自包含:用户信息(如 userId、role)直接编码在 Token 中,服务器无需存储。

  • 客户端存储(通常放在 localStorage 或内存,通过 Authorization 头发送)。

  • 签名防篡改(服务器用密钥验证)。

javascript

// JWT 示例载荷
{
  "sub": "1234567890",
  "name": "Alice",
  "iat": 1516239022,
  "exp": 1516242622
}

优缺点对比

  • ✅ 无状态,适合分布式系统。

  • ✅ 跨语言、跨平台友好。

  • ❌ 一旦签发,无法主动撤销(除非加黑名单,又变回了有状态)。

  • ❌ 载荷体积比 Cookie 大(每次请求都带着)。

最佳实践:取长补短

  • 传统单体应用:Session + Cookie(简单、安全、可控)。

  • REST API / SPA / 移动端:JWT + 刷新令牌 (Refresh Token)。

  • 高安全场景(银行、政务):双重验证 + 短生命周期 Token + 实时撤销能力。

8. 总结

  • Cookie 是浏览器端的存储与自动发送机制,用来携带凭证。

  • Session 是服务器端的数据档案,通过 Session ID 关联。

  • 两者配合,解决了 HTTP 无状态的问题。

  • 安全永远是第一优先级:HttpOnlySecureSameSite、HTTPS、输入校验缺一不可。

  • 随着架构演进,JWT 等无状态 Token 方案在分布式和跨端场景中逐渐成为主角。

记住:没有万能的方案,只有适合当前场景的权衡。下次当你点击“记住我”或打开购物车时,你应该能清晰看到 Cookie 和 Session 在幕后默默协作的身影。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐