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

这篇笔记记录一下我对 JWT 和 Session 区别的理解。
以前我看到登录方案时,经常会听到两句话:
传统项目用 Session。
前后端分离项目用 JWT。这句话有一定现实背景,但如果只这样记,很容易把问题想得太简单。
后来我发现,JWT 和 Session 最大的区别不是谁更新,而是:
登录状态主要保存在服务端,还是主要保存在 token 自己里面。Session 是怎么工作的
Session 登录的典型流程是:
用户提交账号密码
-> 后端校验成功
-> 后端创建一份 Session 数据
-> 后端把 Session ID 写进 Cookie
-> 浏览器后续请求自动带上 Cookie
-> 后端根据 Session ID 查用户状态可以把服务端想象成保存了一张表:
session_id_abc -> 用户 10001,角色 admin,过期时间 ...
session_id_def -> 用户 10002,角色 user,过期时间 ...浏览器只保存一个 ID:
Cookie: session_id=session_id_abc真正的登录状态在服务端。
JWT 是怎么工作的
JWT 登录的典型流程是:
用户提交账号密码
-> 后端校验成功
-> 后端生成一个带签名的 JWT
-> 前端保存 JWT
-> 后续请求带上 JWT
-> 后端验证签名和过期时间
-> 从 Payload 里读取用户身份JWT 自己就携带了身份声明:
{
"sub": "10001",
"role": "admin",
"exp": 1781000000
}后端不一定要查 Session 表,也能知道这个 token 是不是自己签发的、有没有过期、代表哪个用户。
最核心的区别
可以用一句话区分:
Session 是服务端保存状态,客户端保存一个 ID。
JWT 是客户端携带一份可验证的状态声明。表格对比更直观:
| 对比项 | Session | JWT |
|---|---|---|
| 状态主要存在哪里 | 服务端 | token 里 |
| 客户端保存什么 | Session ID | JWT 字符串 |
| 服务端是否需要存储登录态 | 通常需要 | access token 通常不需要 |
| 主动失效是否方便 | 比较方便 | 需要额外机制 |
| token 体积 | 通常很小 | 可能更大 |
| 扩展到多服务 | 要共享 Session | 验签即可识别 |
Session 的优点
Session 的优点是服务端控制力强。
比如用户退出登录:
删除服务端 Session下次浏览器再带同一个 Session ID 来,后端查不到,就认为已经失效。
再比如管理员修改用户权限,服务端可以直接更新 Session 或让旧 Session 失效。
这类操作对 Session 很自然,因为状态本来就在服务端。
Session 的问题
Session 的问题也来自服务端状态。
如果只有一台服务器,问题不大。
但如果有多台服务器:
请求 1 打到服务器 A
请求 2 打到服务器 B如果 Session 只存在服务器 A 的内存里,服务器 B 就不认识这个用户。
常见解决方式有:
- 使用 Redis 统一保存 Session。
- 使用数据库保存 Session。
- 使用负载均衡的粘性会话。
- 把登录状态改成 token 化方案。
所以 Session 不是不能扩展,只是需要额外的共享存储或架构设计。
JWT 的优点
JWT 的优点是更容易做无状态认证。
后端只要拿到 token,就可以验证:
签名是否合法
是否过期
签发方是否正确
用户 ID 是谁多个服务只要共享验证密钥或公钥,就都能识别同一份 token。
这对微服务、网关、移动端 API、第三方接口会比较方便。
比如:
API 网关验证 JWT
-> 解析出 userId
-> 转发给后面的业务服务业务服务不一定每次都要查一个集中式 Session。
JWT 的问题
JWT 最大的问题是主动失效比较麻烦。
假设 JWT 有效期是 7 天。
用户今天退出登录,如果前端只是删除本地 token,服务端并不知道。
只要别人提前拿到了这个 token,在它过期前仍然可能继续使用。
要解决这个问题,就需要额外机制:
- 缩短 access token 有效期。
- 使用 refresh token。
- 服务端维护黑名单。
- 使用 token 版本号。
- 高风险操作重新查库或重新验证。
这时 JWT 也不再是完全无状态了。
安全风险不一样
Session 常见是通过 Cookie 保存 Session ID。
如果 Cookie 设置得好:
HttpOnly; Secure; SameSite=Lax前端脚本不能直接读取 Session ID,能减少 XSS 偷凭证的风险。
JWT 经常被放在 localStorage 里:
localStorage.setItem("token", token);这样写方便,但 JavaScript 可以读取。只要出现 XSS,token 就可能泄漏。
当然,JWT 也可以放到 HttpOnly Cookie 里。
所以不要把问题简化成:
Session 安全,JWT 不安全更准确的是:
安全性取决于保存方式、过期策略、服务端校验和前端防护。权限变化时有什么区别
Session 里如果保存用户权限:
session_id -> role = admin管理员修改权限后,可以直接更新服务端 Session,或者删除旧 Session。
JWT 如果把权限写进 Payload:
{
"role": "admin"
}权限变化后,旧 token 里仍然写着旧角色。
除非:
- token 很快过期。
- 服务端维护版本号。
- 每次请求都根据用户 ID 查最新权限。
所以权限变化频繁的系统,要谨慎把太多权限信息直接写死在长期 JWT 里。
前后端分离一定要用 JWT 吗
不一定。
前后端分离项目也可以使用 Cookie + Session。
只要处理好:
- 跨域 Cookie。
SameSite。- CORS。
- CSRF 防护。
它照样可以工作。
JWT 也不一定只适合前后端分离,传统服务端渲染项目也可以用。
所以技术选择不要只靠项目形态,而要看需求:
- 是否多端登录。
- 是否需要服务端主动控制会话。
- 是否有多服务共享认证。
- 是否要非常方便地注销和踢人。
- 团队更熟悉哪种安全模型。
怎么选
我现在会这样粗略判断。
如果系统是后台管理、单体应用、需要方便踢人和管理登录状态,Session 很合适。
如果系统是 API 服务、多端接入、多个服务共享认证、希望减少集中式会话依赖,JWT 很合适。
如果安全要求很高,也可以混合使用:
短期 access token
长期 refresh token 存服务端
HttpOnly Cookie 保存敏感凭证
服务端保留撤销能力实际项目里,纯 Session 和纯 JWT 都不是唯一答案。
我最后想通的地方
JWT 和 Session 不是新旧技术的简单替代关系。
它们解决的是同一个问题:
请求来了以后,服务器怎么知道你是谁?Session 的回答是:
你给我一个 ID,我去服务端查。JWT 的回答是:
你带一份我签过名的声明,我验证它。一个控制力更集中,一个分发和验证更轻便。
真正选方案时,不要只问“现在流行什么”,而要问:
我需要服务端多强的控制力?
我能接受 token 在多久内自然过期?
我的权限变化是不是必须立刻生效?
我的系统是不是需要多个服务共同识别用户?这些问题想清楚以后,JWT 和 Session 的选择就不再只是背概念了。