一文搞懂 Cookie / Session / JWT
一文搞懂 Cookie / Session / JWT
——它们到底解决了什么问题?
在 Web 开发中,用户登录态几乎是绕不开的问题。
Cookie、Session、JWT 这三个名词经常一起出现,也经常被混着用,甚至被误解。
本文尝试用工程视角,从一个核心问题出发:
状态到底放在哪里?
把它们一次性讲清楚。
一、Cookie 是什么?——它不是“认证机制”
Cookie 的本质
Cookie 是浏览器保存的一组键值对,并在符合规则的请求中自动携带给服务器。
Cookie 的几个关键点
- Cookie 是 客户端存储
- 按 Domain + Path 进行匹配
- 在 HTTP 层表现为:
Cookie: k1=v1; k2=v2
⚠️ Cookie 本身 ≠ 登录态
Cookie 只是一个数据载体,而不是认证机制。
常见误区
❌ Cookie = 登录凭证
✅ Cookie = 承载登录相关标识的容器
例如:
Cookie:
JSESSIONID=abc123 ← 登录相关
theme=dark ← UI 偏好
👉 是否“登录”,并不是 Cookie 决定的,而是服务器的判断逻辑。
二、Session 是什么?——真正的“登录态状态”
Session 的核心思想
登录相关的状态,由服务器保存。
典型 Session 登录流程
- 用户登录成功
- 服务器生成
sessionId - 服务器保存:
sessionId → 用户信息 / 权限 / 过期时间
- 服务器通过
Set-Cookie把sessionId发给浏览器 - 浏览器后续请求自动携带
sessionId - 服务器根据
sessionId查 Session 决定是否放行
Session 的“状态”到底是什么?
Session 的状态不是抽象概念,而是真实存在的数据,例如:
- 用户 ID
- 角色 / 权限
- 登录是否有效
- 最后访问时间
- 是否被踢下线
👉 这些数据全部在服务器端,并且是可变的。
这也是为什么 Session 被称为:
有状态(stateful)
三、Session + Redis:成熟但有代价的方案
在多实例部署时,Session 通常会存入 Redis:
浏览器 → sessionId → Redis → Session 对象
✅ 优点
- 多机共享 Session
- 服务端完全可控
- 可以随时踢人
- 可以动态修改权限
❌ 缺点(不是不能用,而是有成本)
- 每个请求都依赖 Redis
- Redis 成为登录态中枢
- 网络 IO + 反序列化开销
- 多服务间对 Session 结构产生耦合
重要澄清
Session + Redis 是“集中式状态管理”,不是无状态。
⚠️ 市面上一些项目,导入jwt的包,然后把jwt发的token存入redis,每次登录去查询redis,其实思想与这个类似,都是有状态的,整个登录核心全都在redis,都是通过一个序列字符串去判断用户的登录状态。
但必须强调一句:
在 80% 的业务系统中,这依然是一个非常稳妥、成熟的方案。
四、JWT 是什么?——把状态“打包”给客户端
JWT 的核心思想
服务器不保存会话状态,而是把用户身份信息签名后交给客户端随身携带。
JWT 中通常包含什么?
{
"userId": 1,
"roles": ["admin"],
"exp": 1700000000
}
这些内容,本质上和 Session 里的数据是一样的。
JWT 和 Session 的根本区别
不是“信息多 vs 少”,而是:
| 维度 | Session | JWT |
|---|---|---|
| 状态存储 | 服务端 | 客户端 |
| 服务端是否保存会话 | 是 | 否 |
| 鉴权方式 | 查 Session | 本地验签 |
| 可控性 | 强 | 弱 |
| 扩展性 | 一般 | 强 |
👉 JWT 更像是“被签名的 Session 快照”。
五、JWT 为什么被称为“无状态”?
JWT 的“无状态”指的是:
服务器不保存和某个用户登录强相关的会话对象。
但这并不意味着:
- ❌ 服务器没有任何依赖
- ❌ 服务器不保存任何东西
实际上,JWT 必须依赖服务器端密钥。
六、JWT 的密钥
JWT 的安全性来自签名。
1️⃣ 对称签名(HS256)
- 一个
secret - 签名和验证都用它
- 简单、性能好
- 所有服务都要知道 secret
适合:
- 单体应用
- 小系统
- 内部系统
2️⃣ 非对称签名(RS256)
- 私钥签名
- 公钥验证
- 签发权和验证权分离
适合:
- API 网关
- 微服务架构
- 多语言系统
为什么密钥不算“有状态”?
因为密钥是:
- 系统级配置
- 与具体用户无关
- 不随请求变化
👉 JWT 是“无会话状态”,不是“无配置状态”。
七、JWT + Redis 踢人:真的和 Session 一样吗?
如果 JWT 要做到 “立即踢人”,确实需要 Redis。
但关键区别在于:
Session + Redis
- Redis 存完整 Session
- Redis 是 身份真相源
- 每次请求都强依赖 Redis
JWT + Redis
-
JWT 本地完成身份解析
-
Redis 只存:
- 黑名单
- 版本号
- 禁用标记
-
Redis 是 “否决补丁”
👉 JWT + Redis 并不是完全无状态,而是“弱状态 + 本地优先”。
八、为什么 App / 小程序更偏向 JWT?
因为:
- 没有浏览器 Cookie 机制
- 不会自动携带
JSESSIONID - 请求完全由客户端控制
JWT 作为显式 Token:
Authorization: Bearer xxx.yyy.zzz
天然适合:
- App
- 小程序
- 第三方 API
九、如何在 Session 和 JWT 之间做选择?
Session 更适合
- 管理后台
- 内部系统
- 用户规模可控
- 需要频繁踢人、封号
JWT 更适合
- 前后端分离
- App / 小程序
- 微服务架构
- 跨语言 / 跨系统调用
现实中的常态
JWT 负责「你是谁」,
Redis / DB 负责「你还能不能用这个身份」。
十、一句话总结(架构视角)
Cookie 是载体,
Session 和 JWT 是两种「状态放置策略」,
本质区别不在技术,而在控制权与扩展性的取舍。
十一,JWT 真正用法:高并发 + 大用户规模(Access/Refresh 模式)
在高并发场景,如果每次请求都去 Redis 校验 token 是否在黑名单/白名单里,系统就变成“强状态会话”,JWT 的无状态优势(快速验签、少依赖外部存储)会被削弱,同时 Redis 会承受很大 QPS 压力。
因此常见做法是 access_token(短) + refresh_token(长):
- access_token(exp≈10min):用于高频业务请求鉴权
服务端只做验签 + exp 校验 + 解析必要的身份声明(uid/role/scope),通常不查 Redis。 - refresh_token(exp≈30d):代表真正的登录态
服务端把 refresh 记录存 Redis(可按 uid+device 维度),只在刷新时校验。
流程:
-
用户首次登录,下发
access_token+refresh_token,并将 refresh 记录写入 Redis(带 TTL)。 -
客户端调用业务接口时只携带 access token。
-
access token 过期后,客户端调用
/auth/refresh携带 refresh token:- 若 Redis 校验通过,签发新的 access token;
- refresh token 可选轮换(rotation):每次刷新下发新的 refresh 并作废旧的,提升安全性。
踢人策略:
- 常规踢人:撤销/删除 Redis 中的 refresh(或版本号 ver++),用户无法继续刷新;代价是已有 access 可能还能用到过期(例如最多 10 分钟)。
- 实时踢人:需要让每次请求都能感知“被踢”,通常需要在请求链路查 Redis(白名单/ver 或 access jti 黑名单)。很多系统只对“敏感接口”启用该强校验,普通接口保持无状态以换取性能。
更多推荐



所有评论(0)