一文搞懂 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 登录流程

  1. 用户登录成功
  2. 服务器生成 sessionId
  3. 服务器保存:
sessionId → 用户信息 / 权限 / 过期时间
  1. 服务器通过 Set-CookiesessionId 发给浏览器
  2. 浏览器后续请求自动携带 sessionId
  3. 服务器根据 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 维度),只在刷新时校验。

流程:

  1. 用户首次登录,下发 access_token + refresh_token,并将 refresh 记录写入 Redis(带 TTL)。

  2. 客户端调用业务接口时只携带 access token。

  3. access token 过期后,客户端调用 /auth/refresh 携带 refresh token:

    • 若 Redis 校验通过,签发新的 access token;
    • refresh token 可选轮换(rotation):每次刷新下发新的 refresh 并作废旧的,提升安全性。

踢人策略:

  • 常规踢人:撤销/删除 Redis 中的 refresh(或版本号 ver++),用户无法继续刷新;代价是已有 access 可能还能用到过期(例如最多 10 分钟)。
  • 实时踢人:需要让每次请求都能感知“被踢”,通常需要在请求链路查 Redis(白名单/ver 或 access jti 黑名单)。很多系统只对“敏感接口”启用该强校验,普通接口保持无状态以换取性能。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐