JWT vs Session vs Cookie:认证方案怎么选
认证方案之争,先厘清三个概念
做后端时,几乎每个人都会在某个时刻纠结:登录态用 JWT 还是用 Session?而 Cookie 又和这俩是什么关系?
先破除一个最常见的误区:JWT、Session、Cookie 根本不在同一个维度上。 JWT 是一种”令牌格式”,Session 是一种”服务端会话机制”,Cookie 是一种”浏览器存储与自动携带机制”。它们不是三选一的对立关系,而是可以互相组合——JWT 可以放进 Cookie 里,Session 也可以用一个 Cookie 承载会话 ID。
所以真正要比的,是”用什么承载登录态”的两大路线:服务端状态(Session) 与客户端自包含令牌(JWT),而 Cookie 是它们都可能用到的”搬运工”。这篇把这条线讲清楚,最后落到”什么时候选哪个”,并说明星点 JWT 工具在开发调试环节能帮上什么忙。
先分清三者的身份
| 概念 | 是什么 | 类比 |
|---|---|---|
| Session | 服务端保存会话数据的机制,客户端只持有会话 ID | 银行给你一张排号,明细都存在银行 |
| JWT | 客户端自包含的令牌,数据(Claims)就写在令牌里 | 你随身带一张写满信息的通行证 |
| Cookie | 浏览器存储并自动携带的键值机制 | 一个会自动贴上传送带的贴纸 |
这张表是理解一切的基础:Session 的”状态”在服务端,JWT 的”状态”在客户端令牌里,Cookie 只是负责”存和带”。
原理对比:数据存在哪,决定了后面的所有差异
Session:服务端记着你是谁
用户登录后,服务端生成一个会话,把用户信息、权限等存进服务端(内存、Redis 等),只把一个随机的 session_id 发回给客户端。之后客户端每次请求带上这个 ID(通常通过 Cookie),服务端拿 ID 去查会话。
关键点:真正的数据在服务端,客户端只拿一个无意义的 ID。 所以用户信息改了、注销了,服务端删掉会话即可,客户端拿着的 ID 立刻失效。
JWT:客户端自己带着”证明”
用户登录后,服务端把用户信息(如 sub、role、exp)打包成 JSON,用密钥签名,生成一个 JWT 发给客户端。之后每次请求带上这个 JWT(通常放在 Authorization: Bearer <token> 头里),服务端验签、读 Claims,不需要查任何存储。
关键点:数据就在令牌里,验签通过即可信,服务端无状态。 代价是:令牌一旦发出去,服务端无法主动让它”立刻失效”(除非引入黑名单之类的额外状态)。
Cookie:两者都可能用到的搬运工
Cookie 既可以是”存 session_id”的载体,也可以是”存 JWT”的载体。它的特点是:浏览器会自动在每次请求里带上它(受 Domain、Path、HttpOnly、Secure、SameSite 等属性约束)。这意味着 Cookie 方案天然省去了前端”手动加头”的步骤,但也带来了 CSRF 等浏览器自动携带带来的风险。
一张总览对比表
| 维度 | Session | JWT |
|---|---|---|
| 状态位置 | 服务端 | 客户端(令牌内) |
| 服务端是否有状态 | 有(需存储) | 无(可无状态) |
| 水平扩展 | 需共享 Session 存储(如 Redis) | 天然易扩展,各节点独立验签 |
| 吊销/登出 | 立即(删会话) | 难立即(需黑名单等额外机制) |
| 令牌大小 | 小(只存 ID) | 较大(带 Claims) |
| 服务端查询成本 | 每次请求查存储 | 无查询,只验签 |
| 跨服务/跨域 | 需共享会话 | 天然适合(微服务、跨域 API) |
| 主要安全风险 | CSRF、Session 劫持 | 令牌泄露、无法即时吊销、alg 攻击 |
| 典型场景 | 传统单体 Web 应用 | SPA、移动端、微服务、无状态 API |
这张表基本决定了选型。下面把最关键的几个点展开。
扩展性:JWT 真正的杀手锏
扩展性是 JWT 相对 Session 最硬的优势。Session 方案的会话数据在服务端,一旦服务扩展成多台机器,就得引入共享 Session 存储(比如所有节点都读同一个 Redis),否则用户请求落到另一台机器上就”不认识”了。这是额外的一套基础设施和复杂度。
JWT 天然无状态:每个服务节点只要持有验签用的密钥(或公钥),就能独立验证令牌、读取 Claims,不需要彼此之间共享任何会话状态。 在微服务架构、跨域 API、网关鉴权这些场景里,这个优势是决定性的——它把一个”集中式状态”问题变成了”分布式无状态”问题。
代价也在这里:无状态换来的是”吊销难”。 Session 里,服务端删一条记录用户就登出了;JWT 里,令牌在客户端手里,服务端想让它失效,只能靠”短过期时间 + 刷新令牌”、“黑名单/白名单”这些额外的机制来补——本质上又引入了一点状态。
Token 的存储位置:Cookie、localStorage、内存
选定 JWT 之后,还有一个隐藏的决定:Token 存在哪。 这直接决定安全性,甚至决定 CSRF/XSS 的攻防格局。
| 存储位置 | 优点 | 缺点 | 主要风险 |
|---|---|---|---|
| localStorage | 前端易用、可跨标签 | JS 可直接读取 | XSS 窃取 |
| HttpOnly Cookie | JS 读不到、自动携带 | 需后端配合下发 | CSRF |
| 内存(JS 变量) | 最安全,刷新即丢 | 刷新/关闭页面就没了 | 体验差,需配合刷新机制 |
三个位置的权衡:
- localStorage:最方便,但一旦被 XSS 注入,脚本能直接
localStorage.getItem('token')拿走令牌。 - HttpOnly Cookie:JS 读不到,挡住了 XSS 窃取,但因为浏览器自动携带,又要重新面对 CSRF(需要用 SameSite、CSRF Token 等手段补)。
- 内存:最安全,但用户体验差,通常要配合”刷新令牌 + 静默重登”。
没有完美答案,只有取舍。 高安全要求的场景,很多人选 HttpOnly Cookie + SameSite=Lax/Strict + CSRF Token;偏便捷的场景,用 localStorage 但把 XSS 防护做到位。安全不取决于”用 JWT 还是 Session”,而取决于”放哪、怎么配”。
刷新令牌:Access Token + Refresh Token 的经典组合
为了解决 JWT”吊销难、长期有效不安全”的问题,业界普遍用双令牌结构:
| 令牌 | 作用 | 有效期 | 特点 |
|---|---|---|---|
| Access Token | 实际调接口用的凭证 | 短(如 15 分钟) | 泄露影响窗口小 |
| Refresh Token | 换取新 Access Token | 长(如 30 天) | 可被服务端吊销 |
流程是:Access Token 短命,过期后用 Refresh Token 去换新的;Refresh Token 存在服务端,可以主动吊销。这样既保住了 JWT 无状态验签的优点,又拿回了”能踢人”的能力——吊销 Refresh Token 就等于登出。这是 JWT 方案在真实生产环境里几乎必配的一环。
这也侧面说明:纯粹的”无状态 JWT”在实践里很少单独存在,总会为吊销、刷新引入一点状态。
把 JWT 拆开看:Header / Payload / Signature
既然要选 JWT,最好把它结构看明白。一个标准 JWT 由三段组成,用 . 连接:
Header.Payload.Signature| 段 | 内容 | 是否可读 |
|---|---|---|
| Header | 算法 alg、类型 typ、密钥 ID kid | 是(Base64URL 编码的 JSON) |
| Payload | 标准 Claims + 自定义字段 | 是(Base64URL 编码的 JSON) |
| Signature | 对前两段的签名结果 | 否(二进制,只能用于验证) |
几个高频 Claims 的含义:
| Claim | 含义 | 典型用途 |
|---|---|---|
sub | 主体(Subject),通常是用户 ID | 标识”这个 Token 是谁的” |
exp | 过期时间(Expiration) | 控制 Token 有效期 |
iat | 签发时间(Issued At) | 记录签发时刻 |
nbf | 生效时间(Not Before) | “此前不可用” |
iss / aud | 签发方 / 接收方 | 校验 Token 该不该被这个服务接受 |
理解这个结构后,一个重要结论就清楚了:前两段是明文可解码的,谁拿到都能读。所以 JWT 里别放敏感信息(密码、身份证号、手机号)——它只是”防篡改”,不是”防偷看”。要真正加密内容,得用 JWE(加密 Token),那是另一回事。
调试时想看 Token 里到底有什么、是否过期,用 JWT 在线解析 粘贴即解析;想判断签名是否有效、是否被篡改,用 JWT 签名验证。这正好对应 JWT 的两层特性:可读(解析)与可信(验签)。
安全性:各自的软肋
安全上没有银弹,两者各有软肋。
| 风险 | Session 场景 | JWT 场景 |
|---|---|---|
| 凭据泄露 | session_id 泄露可被冒用 | JWT 泄露可被冒用(直到过期) |
| CSRF | 风险较高(Cookie 自动携带) | 用 Authorization 头时天然规避 CSRF |
| XSS 窃取 | 可窃取 Cookie | 可窃取 localStorage 里的 JWT |
| 即时吊销 | 容易 | 难(需额外机制) |
| 算法攻击 | 无 | alg:none、密钥混淆等 |
几个关键结论:
- CSRF:Session 方案里 Cookie 自动携带,攻击者可以在别的站点诱导浏览器发请求,带上受害者的 Cookie。JWT 若放在
Authorization头里(而非 Cookie),则天然规避了 CSRF。但如果 JWT 也放进 Cookie,CSRF 风险又回来了——安全不取决于”用 JWT 还是 Session”,而取决于”放在哪、怎么配”。 - XSS:把 JWT 存 localStorage,一旦被 XSS 注入,脚本可以直接读到令牌。存 HttpOnly Cookie 可以挡住 JS 读取,但又要重新面对 CSRF。没有完美方案,只能权衡。
- JWT 特有的坑:
alg:none(无签名,可被伪造)、RS/HS 算法混淆、密钥过弱、exp设太长。这些是 JWT 实现层面独有的风险,需要团队格外注意。一个 JWT 能否被”解析出来”和它”签名是否有效”是两回事——解析谁都能做,验证才需要密钥。
关于 alg 和算法混淆
JWT 的 Header 里声明了 alg,但有些服务器错误地信任客户端声明的算法。攻击者把 alg 改成 none,就能伪造一个”无签名”却能被接受的令牌;或者利用 RS/HS 混淆,拿公钥当 HMAC 密钥去伪造签名。这些都属于 JWT 特有的安全雷区,也是为什么验证时永远要以服务端自己信任的算法集合为准,而不是照单全收 Header 里声明的算法。
什么时候选哪个:决策表
| 你的场景 | 推荐 | 理由 |
|---|---|---|
| 传统单体 Web 应用、服务端渲染 | Session | 吊销容易、实现成熟、数据在服务端更可控 |
| 前后端分离 SPA + 后端 API | JWT | 无状态、天然适配跨域与移动端 |
| 微服务架构、多服务共享鉴权 | JWT | 各节点独立验签,无需共享状态 |
| 移动端 App(无 Cookie 概念) | JWT | 用 Header 携带令牌最自然 |
| 需要即时登出、强控在线状态 | Session(或 JWT + 黑名单) | 吊销能力优先 |
| 第三方授权 / 单点登录 | JWT(OAuth/OIDC 生态) | 标准成熟,广泛互认 |
再给一个简化版口诀:“要服务端能随时踢人、单体应用”选 Session;“要无状态、跨服务、移动端、微服务”选 JWT。 两者不是非黑即白,很多系统会混用——Session 管传统 Web 端,JWT 管 API 和移动端。
落到星点:JWT 开发调试的辅助
无论你选 JWT 还是 Session,只要涉及 JWT,开发调试就绕不开”看懂 Token、验证签名”。星点 JWT 系列正好补上这块:
- JWT 在线解析:粘贴即解析 Header/Payload/Claims,自动判断
exp是否过期——调试时一眼看出”这 Token 是不是过期了”,比肉眼盯eyJ...快得多; - JWT 签名验证:输入 Token 与 Secret/公钥,判断签名是否有效、是否被篡改——对应前文”解析不等于验证”的关键区分;
- JWT 生成器:本地生成 HS/RS/ES 各算法 Token,用于联调测试;
- JWT → Authorization Header:把 Token 拼成
Authorization: Bearer请求头,配合 HTTP 请求测试器 直接调接口。
所有 JWT 工具都在浏览器本地完成,Token 与密钥不上传——调试含真实 Token 的鉴权问题时,不会把敏感凭据交到第三方服务器手里。
常见问题(FAQ)
JWT 和 Cookie 是对立的吗?
不是。Cookie 是浏览器存储/携带机制,JWT 是令牌格式,JWT 完全可以放进 Cookie 里。真正对立的是"服务端状态(Session)"与"客户端令牌(JWT)"两条路线。JWT 是无状态的吗?
验签本身无状态(各节点独立验证),但"吊销"天然需要引入额外状态(黑名单等)。所以准确说是"服务端可无状态,代价是吊销难"。JWT 一定比 Session 安全吗?
不一定。安全取决于放在哪、怎么配(HttpOnly/Secure/SameSite、密钥强度、过期时间)。JWT 放 Authorization 头能规避 CSRF,但放 localStorage 又怕 XSS;没有绝对安全。JWT 能解析出来就说明签名有效吗?
不能。解析只是解码 Header/Payload,任何拿到 Token 的人都能做。签名是否有效必须用密钥重新验签,这是两件完全不同的事。Token 存 localStorage 还是 Cookie?
localStorage 方便但怕 XSS;HttpOnly Cookie 挡 XSS 但重新面对 CSRF;内存最安全但体验差。按你的安全要求与 XSS/CSRF 防护能力权衡。为什么会有 Access Token 和 Refresh Token?
Access Token 短命(泄露影响小),Refresh Token 长命且可被服务端吊销。用 Refresh Token 换新 Access Token,既保无状态验签,又拿回"能踢人"的能力。alg:none 是什么攻击?
攻击者把 JWT Header 的 alg 改成 none,让服务端跳过签名验证,从而伪造令牌。防范:服务端永远按自己信任的算法集合验签,不照单全收 Header 声明的算法。为什么有的 Token 解析后 Payload 是一堆乱码?
那可能是 JWE(加密 Token),内容被密文包裹,需先解密才能读到明文,超出了普通"解析"的能力范围。Session 方案怎么扩展到多台服务器?
需要共享 Session 存储(如所有节点读同一个 Redis),这是它相比 JWT 多出来的基础设施复杂度。CSRF 和 XSS 分别防什么?
CSRF 是诱导浏览器用受害者的凭证发请求(Session + Cookie 场景高风险);XSS 是往页面注入脚本窃取数据(威胁 localStorage 里的 JWT)。两者防护手段不同。Token 会被上传吗?
星点 JWT 工具不会。所有解析、验证、生成都在浏览器本地完成,不产生网络请求,Token 与密钥不上传。JWT 里能放敏感信息吗?
不建议。JWT 前两段是可解码的明文,谁拿到都能读,它只防篡改不防偷看。敏感信息(密码、证件号等)绝不应放进 JWT;要加密内容需用 JWE。JWT 过期了会自动失效吗?
过期(exp)只是服务端验签时的一个判断依据,服务端不验 exp,令牌就一直"可用"。所以服务端必须显式校验 exp,否则过期时间形同虚设。Session 就一定比 JWT 慢吗?
不一定。Session 每次请求要查存储(Redis 等),多一次网络往返;JWT 只需本地验签。但现代 Redis 极快,对大多数应用这个差距可忽略,不必为了"性能"强行选 JWT。能同时用 Session 和 JWT 吗?
能,很多系统就是这样:传统 Web 端用 Session + Cookie,移动端和对外 API 用 JWT。两者按场景并存,不冲突。JWT 适合跨域场景吗?
适合。JWT 放在 Authorization 头里,不受浏览器同源策略的 Cookie 限制,天然适合跨域 API 和微服务间的鉴权,这是它相对 Session 的一大优势。选择认证方案要先问自己什么问题?
先问三点:是否需要即时吊销、是否有跨服务/跨域需求、客户端是浏览器还是移动端。要即时吊销+单体就选 Session,要无状态+跨服务/移动端就选 JWT。JWT 过期时间设多长合适?
Access Token 建议短(如 15 分钟),配合 Refresh Token 长(如 30 天)换取。短期过期能把泄露的影响窗口压到最小,长令牌则交给可吊销的 Refresh Token 管理。刷新令牌和黑名单冲突吗?
不冲突,反而互补:Refresh Token 可被服务端吊销(相当于登出),黑名单用于立刻禁用某个 Access Token。二者都是给"无状态 JWT"补回状态能力的手段。一分钟速查清单
定认证方案前,用这三个问题收敛:
- 要不要服务端随时踢人?要 → 优先 Session(或 JWT + Refresh Token 吊销);
- 有没有跨服务、跨域、移动端的诉求?有 → 优先 JWT;
- 客户端是浏览器还是 App?浏览器可 Session+HttpOnly Cookie,App 用 JWT 更自然。
再记牢两条铁律:JWT 别放敏感信息、验签别照单全收 Header 声明的算法。
总结
JWT、Session、Cookie 不是三选一,而是”令牌格式 vs 会话机制 vs 存储载体”。选型抓住两条:要即时吊销、单体应用选 Session;要无状态、跨服务、移动端、微服务选 JWT。 Cookie 是两者都可能用到的搬运工,Token 存哪、怎么配决定安全上限。涉及 JWT 的开发调试,用星点 JWT 在线解析 看懂 Token、用 JWT 签名验证 判断真伪,全程本地处理、凭据不上传。
立即体验:访问 JWT 在线解析 / 调试器
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!