session_token 和 Cookie:你以为你登录了,其实你只是拿着一张票
session_token 和 Cookie:你以为你登录了,其实你只是拿着一张票
很多刚接触 Web 开发或者好奇上网原理的朋友,经常把 Cookie 和 Session 混为一谈。其实在技术底层,它们分工明确:Cookie 是搬运工,Session Token 是身份证。
如果把你访问网站比作去一家高级私人会所,这个过程大概是这样的:
1. Cookie:会所发给你的“临时胸卡”
当你第一次访问一个网站(会所)时,服务器根本不知道你是谁。HTTP 协议是“无状态”的,这意味着服务器像个患了严重失忆症的接待员,你每点一个页面,他都会问你:“你是谁?请重新出示证件。”
为了不用每次点击都输入账号密码,服务器在验证你的身份后,会发给你一个 Cookie。
Cookie 就像是一张胸卡。它被保存在你的浏览器(本地)里。下次你请求页面时,浏览器会自动把这张胸卡贴在请求头(Request Header)里发给服务器。服务器一看:“哦,胸卡号 123,是那个叫 Bosh 的老板,让他进去。”
Cookie 的特点: - 存在本地:由浏览器管理。 - 自动发送:只要域名匹配,浏览器每次请求都会自动带上。 - 容量极小:通常只有 4KB 左右,塞不下太多东西。
2. session_token:会所后台的“会员档案号”
但问题来了:如果把所有用户信息(权限、购物车、个人偏好)都写在 Cookie 这张胸卡上怎么办?
首先,胸卡太小,塞不下。其次,极度不安全。如果你的胸卡上写着
role=admin,任何懂一点 F12
的人都可以把自己的胸卡改成
admin,直接黑进你的后台。
于是,服务器引入了 Session (会话) 机制。
服务器不再把敏感信息发给你,而是在自己的内存或数据库(比如 Redis)里开辟一块空间,记录你的所有信息,并给这块空间起个唯一的 ID,这就是 session_token(或 session ID)。
现在,服务器发给你的 Cookie
里只包含一个东西:session_id=abc123xyz。
这个 token 就像是一张“存包票”或者“档案索引号”。它本身不包含任何个人信息,它只是一个指向服务器后台档案的“指针”。
完整流程是这样的: 1. 你输入账号密码 \(\rightarrow\) 发给服务器。 2.
服务器验证通过 \(\rightarrow\)
在后台创建 Session \(\rightarrow\) 生成
session_token \(\rightarrow\) 把 token 放入 Cookie
发回浏览器。 3. 浏览器存储 Cookie \(\rightarrow\)
之后每次请求都带上这个 token。 4. 服务器收到 token \(\rightarrow\) 去后台查这个 ID
对应哪个用户 \(\rightarrow\)
返回对应的内容。
3. 为什么得区分开?(架构设计的权衡)
你可能会问:为什么不直接用 Token,非要套一层 Cookie?
因为 Cookie 提供了自动化。如果不用
Cookie,你得在 JavaScript 里手动地把 token 塞进每一个 API 请求的
Header 里(比如 Authorization: Bearer ***)。Cookie
是浏览器的原生行为,只要设置好,你不需要写一行代码,浏览器就会帮你处理。
但这种便捷带来了巨大的安全漏洞。
4. 这里的坑:安全漏洞与黑客手段
既然 Cookie 会自动发送,那么黑客就开始想办法偷这张“胸卡”。
XSS (跨站脚本攻击)
黑客通过在网页里植入一段 JS 代码,直接调用
document.cookie。如果你的 session_token
就在这里,黑客直接把它发到自己的服务器上。结果:黑客现在拥有了你的
session_token,他不需要密码,直接就能以你的身份登录。这就是“会话劫持”。
对策: 给 Cookie 加上 HttpOnly
标志。这样 JS 代码就没法读取这个
Cookie,只能由浏览器在请求时发送。
CSRF (跨站请求伪造)
这是 Cookie “自动发送”特性的反噬。 假设你登录了银行网站 \(\text{Bank.com}\),Cookie
里存着你的 token。此时你访问了一个钓鱼网站 \(\text{Evil.com}\),页面上有一个隐藏的按钮或脚本,向
\(\text{Bank.com}\) 发起了一个
transfer_money 请求。 由于浏览器看到请求目标是
\(\text{Bank.com}\),它会自动把你的银行
Cookie 带上。银行服务器一看:“token
正确,是老板本人发起的请求”,于是钱就被转走了。
对策: 使用 SameSite
属性(限制跨站发送)或在请求中加入随机的 CSRF Token。
5. 现代演进:JWT 与无状态架构
随着前后端分离和微服务流行,传统的 Session 机制遇到了瓶颈:服务器压力太大。
如果你的网站有 100 万个在线用户,服务器得在内存里存 100 万个 Session。如果是有多个服务器节点(集群),你还得搞一个统一的 Redis 集群来共享 Session,否则用户在 A 服务器登录,跳转到 B 服务器就失效了。
于是 JWT (JSON Web Token) 出现了。
JWT 的核心理念是:把身份证信息加密后直接发给用户,服务器不再存档案。
JWT
就像是一张带防伪水印的电子通行证。它包含: -
Header: 算法信息。 - Payload: 用户
ID、过期时间等(明文,但被签名了)。 - Signature:
服务器用私钥生成的签名。
服务器收到 JWT 后,不需要查数据库,只需要用私钥验签。如果签名正确,就说明这张票是真的。
JWT vs Session: - Session:服务器记得你是谁(有状态),安全但费资源。 - JWT:凭票入场,服务器不记得你是谁,只认票(无状态),省资源但撤销困难(一旦发出去,除非到期,否则很难强制让某个 token 失效)。
6. 小H的总结
简单来说: - Cookie 是浏览器和服务器之间传输数据的通道。 - session_token 是存储在通道里的密钥,用来在服务器端检索你的状态。
作为开发者,不要迷信某种方案。如果你做的是传统的单体 Web
应用,HttpOnly Cookie + Redis Session
是最稳妥的;如果你做的是大规模分布式 API 或移动端
App,JWT 或 OAuth2 才是正解。
最重要的一点:永远不要在 Cookie 或 Token 里存明文密码,除非你想在第二天看到自己的域名出现在黑客的公开列表里。
| 本文由 BOSH 的博客助手 HerMes 整理 📝 |
|---|
| 原文链接:用户提供科普需求 |