认证鉴权

从 JWT 到 Session 理解两种登录状态保存方式

这篇笔记记录一下我对 JWT 和 Session 区别的理解。 以前我看到登录方案时,经常会听到两句话: 这句话有一定现实背景,但如果只这样记,很容易把问题想得太简单。 后来我发现,JWT 和 Session 最大的区别不是谁更新,而是: Session 是怎么工作的 Session 登录的典型流程是: 可以把服务端想

2026年6月13日7 分钟阅读
从 JWT 到 Session 理解两种登录状态保存方式

这篇笔记记录一下我对 JWT 和 Session 区别的理解。

以前我看到登录方案时,经常会听到两句话:

text
传统项目用 Session。
前后端分离项目用 JWT。

这句话有一定现实背景,但如果只这样记,很容易把问题想得太简单。

后来我发现,JWT 和 Session 最大的区别不是谁更新,而是:

text
登录状态主要保存在服务端,还是主要保存在 token 自己里面。

Session 是怎么工作的

Session 登录的典型流程是:

text
用户提交账号密码
  -> 后端校验成功
  -> 后端创建一份 Session 数据
  -> 后端把 Session ID 写进 Cookie
  -> 浏览器后续请求自动带上 Cookie
  -> 后端根据 Session ID 查用户状态

可以把服务端想象成保存了一张表:

text
session_id_abc -> 用户 10001,角色 admin,过期时间 ...
session_id_def -> 用户 10002,角色 user,过期时间 ...

浏览器只保存一个 ID:

http
Cookie: session_id=session_id_abc

真正的登录状态在服务端。

JWT 是怎么工作的

JWT 登录的典型流程是:

text
用户提交账号密码
  -> 后端校验成功
  -> 后端生成一个带签名的 JWT
  -> 前端保存 JWT
  -> 后续请求带上 JWT
  -> 后端验证签名和过期时间
  -> 从 Payload 里读取用户身份

JWT 自己就携带了身份声明:

json
{
  "sub": "10001",
  "role": "admin",
  "exp": 1781000000
}

后端不一定要查 Session 表,也能知道这个 token 是不是自己签发的、有没有过期、代表哪个用户。

最核心的区别

可以用一句话区分:

text
Session 是服务端保存状态,客户端保存一个 ID。
JWT 是客户端携带一份可验证的状态声明。

表格对比更直观:

对比项SessionJWT
状态主要存在哪里服务端token 里
客户端保存什么Session IDJWT 字符串
服务端是否需要存储登录态通常需要access token 通常不需要
主动失效是否方便比较方便需要额外机制
token 体积通常很小可能更大
扩展到多服务要共享 Session验签即可识别

Session 的优点

Session 的优点是服务端控制力强。

比如用户退出登录:

text
删除服务端 Session

下次浏览器再带同一个 Session ID 来,后端查不到,就认为已经失效。

再比如管理员修改用户权限,服务端可以直接更新 Session 或让旧 Session 失效。

这类操作对 Session 很自然,因为状态本来就在服务端。

Session 的问题

Session 的问题也来自服务端状态。

如果只有一台服务器,问题不大。

但如果有多台服务器:

text
请求 1 打到服务器 A
请求 2 打到服务器 B

如果 Session 只存在服务器 A 的内存里,服务器 B 就不认识这个用户。

常见解决方式有:

  • 使用 Redis 统一保存 Session。
  • 使用数据库保存 Session。
  • 使用负载均衡的粘性会话。
  • 把登录状态改成 token 化方案。

所以 Session 不是不能扩展,只是需要额外的共享存储或架构设计。

JWT 的优点

JWT 的优点是更容易做无状态认证。

后端只要拿到 token,就可以验证:

text
签名是否合法
是否过期
签发方是否正确
用户 ID 是谁

多个服务只要共享验证密钥或公钥,就都能识别同一份 token。

这对微服务、网关、移动端 API、第三方接口会比较方便。

比如:

text
API 网关验证 JWT
  -> 解析出 userId
  -> 转发给后面的业务服务

业务服务不一定每次都要查一个集中式 Session。

JWT 的问题

JWT 最大的问题是主动失效比较麻烦。

假设 JWT 有效期是 7 天。

用户今天退出登录,如果前端只是删除本地 token,服务端并不知道。

只要别人提前拿到了这个 token,在它过期前仍然可能继续使用。

要解决这个问题,就需要额外机制:

  • 缩短 access token 有效期。
  • 使用 refresh token。
  • 服务端维护黑名单。
  • 使用 token 版本号。
  • 高风险操作重新查库或重新验证。

这时 JWT 也不再是完全无状态了。

安全风险不一样

Session 常见是通过 Cookie 保存 Session ID。

如果 Cookie 设置得好:

http
HttpOnly; Secure; SameSite=Lax

前端脚本不能直接读取 Session ID,能减少 XSS 偷凭证的风险。

JWT 经常被放在 localStorage 里:

ts
localStorage.setItem("token", token);

这样写方便,但 JavaScript 可以读取。只要出现 XSS,token 就可能泄漏。

当然,JWT 也可以放到 HttpOnly Cookie 里。

所以不要把问题简化成:

text
Session 安全,JWT 不安全

更准确的是:

text
安全性取决于保存方式、过期策略、服务端校验和前端防护。

权限变化时有什么区别

Session 里如果保存用户权限:

text
session_id -> role = admin

管理员修改权限后,可以直接更新服务端 Session,或者删除旧 Session。

JWT 如果把权限写进 Payload:

json
{
  "role": "admin"
}

权限变化后,旧 token 里仍然写着旧角色。

除非:

  • token 很快过期。
  • 服务端维护版本号。
  • 每次请求都根据用户 ID 查最新权限。

所以权限变化频繁的系统,要谨慎把太多权限信息直接写死在长期 JWT 里。

前后端分离一定要用 JWT 吗

不一定。

前后端分离项目也可以使用 Cookie + Session。

只要处理好:

  • 跨域 Cookie。
  • SameSite
  • CORS。
  • CSRF 防护。

它照样可以工作。

JWT 也不一定只适合前后端分离,传统服务端渲染项目也可以用。

所以技术选择不要只靠项目形态,而要看需求:

  • 是否多端登录。
  • 是否需要服务端主动控制会话。
  • 是否有多服务共享认证。
  • 是否要非常方便地注销和踢人。
  • 团队更熟悉哪种安全模型。

怎么选

我现在会这样粗略判断。

如果系统是后台管理、单体应用、需要方便踢人和管理登录状态,Session 很合适。

如果系统是 API 服务、多端接入、多个服务共享认证、希望减少集中式会话依赖,JWT 很合适。

如果安全要求很高,也可以混合使用:

text
短期 access token
长期 refresh token 存服务端
HttpOnly Cookie 保存敏感凭证
服务端保留撤销能力

实际项目里,纯 Session 和纯 JWT 都不是唯一答案。

我最后想通的地方

JWT 和 Session 不是新旧技术的简单替代关系。

它们解决的是同一个问题:

text
请求来了以后,服务器怎么知道你是谁?

Session 的回答是:

text
你给我一个 ID,我去服务端查。

JWT 的回答是:

text
你带一份我签过名的声明,我验证它。

一个控制力更集中,一个分发和验证更轻便。

真正选方案时,不要只问“现在流行什么”,而要问:

text
我需要服务端多强的控制力?
我能接受 token 在多久内自然过期?
我的权限变化是不是必须立刻生效?
我的系统是不是需要多个服务共同识别用户?

这些问题想清楚以后,JWT 和 Session 的选择就不再只是背概念了。