JWT 和 Session 的分歧不在格式,而在三件事:登录状态放在哪、谁来校验、出问题怎么收回。很多人搜这个问题时,还没确认自己系统当前下发的是 cookie 还是 Authorization 头,讨论就一直停在名词层面。先核对现状,再按场景选。
如果服务只有一两个实例、需要随时把某个用户踢下线,Session 加服务端存储更省事;如果要跨多个服务、多种客户端共用一套鉴权,JWT 自包含验签更顺,代价是吊销存在延迟。判断动作是抓一次登录响应,看凭据形态和服务端有没有对应存储;验证方式是登出后再用同一凭据请求一次受保护接口。两边都不无条件更优,改动前先在测试环境把两条链路各跑一遍。
请简述cookie token Session和JWT四种身份机制的区别
cookie 是传输载体,token 是凭据形态,Session 是服务端保存的状态,JWT 是一种带签名的自包含 token 格式。四者不在同一层,所谓“区别”必须先落到你自己的链路上。这一步对应排查的第一个动作:确认现状。按下面顺序走,通常一次就能定位:
- 浏览器 DevTools → Network,点一次登录请求,看 Response Headers 有没有
Set-Cookie,且值里是否带 HttpOnly。 - 看响应体里有没有
access_token、id_token一类字段,或前端是否把它写进了 localStorage。 - 看后续受保护请求带的是
Cookie:还是Authorization: Bearer。 - 到服务端确认有没有会话存储:Redis key、数据库 session 表、进程内存 Map。
- 如果是 JWT,解出 header 看
alg,解出 payload 看exp、iss、aud是否存在。
抓一次登录响应成本最低,命令可以直接改域名后执行:
curl -i -s -X POST https://your-app.example.com/api/login \
-H 'Content-Type: application/json' \
-d '{"username":"demo","password":"***"}'
重点看两处:Response Headers 里的 Set-Cookie,以及响应体里的 token 字段。如果拿到的是 JWT,可以解开 payload 核对声明,注意这只是 base64url 解码,不是解密:
TOKEN='粘贴 access token'
python3 -c "import sys,base64,json;s=sys.argv[1].split('.')[1];s+='='*(-len(s)%4);print(json.dumps(json.loads(base64.urlsafe_b64decode(s)),indent=2,ensure_ascii=False))" "$TOKEN"
这里有个常见坑:把 JWT 当成加密容器,往 payload 里塞手机号、角色明细,甚至 session id。base64url 只是编码,任何拿到 token 的人都能解开;把 session id 塞进 JWT 再靠验签保护它,相当于既丢了无状态的好处,又没做到服务端即时吊销,两边不占。
JWT和cookie Session登录的区别
这一步对应排查的第二步:确认登录成功后凭据存在哪里、登出时谁失效。Session 登录是服务端生成随机 id 并写入存储,浏览器只拿到一个不透明字符串;JWT 登录是服务端签发签名令牌返回客户端,服务端默认不记录任何东西。
核对位置有三处,缺一不可:登录接口响应(Set-Cookie 还是响应体 token)、服务端存储(有没有对应写入)、登出接口(删存储还是只清前端)。存储侧可以这样看:
redis-cli --scan --pattern 'sess:*' | head -n 5
redis-cli ttl 'sess:把上面扫到的后缀粘进来'
redis-cli dbsize
更直接的验证是登出后再打一次受保护接口:

curl -i -s https://your-app.example.com/api/me -H 'Cookie: session=粘贴登出前的值'
返回 401,说明服务端确实失效了;仍然返回 200,说明登出只动了前端,或者 JWT 还在有效期内没被吊销。JWT 场景下后者是预期行为,不是 bug,但需要在设计时用较短的 exp 加 refresh token 把这个窗口收窄。
JWT和Session:各自站在什么位置
Session 站在服务端一侧:登录状态由服务端保存,客户端拿到的只是引用。它擅长需要即时踢人、用户规模可控、已经有共享存储的场景。核对口径有两个可查点:存储里 key 的 TTL 是否与业务会话时长一致;cookie 是否带上 HttpOnly、Secure、SameSite 三个属性——前两个防脚本读取和明文传输,第三个影响跨站请求行为。
JWT 站在令牌一侧:状态随令牌走,校验方只需验签。它擅长服务间调用、Web 与 App 共用一套鉴权、不想维护会话存储的场景。核对口径是 JWT 规范里定义的注册声明有没有被真正使用,尤其是 exp、iss、aud、nbf,参数名在 RFC 7519 中有完整定义,可以直接对照你线上解出来的 payload。如果 iss 和 aud 都是空的,多半只做了签名没做签发方与受众校验,这类实现的风险要单独评估。
JWT和Session:性能、成本、运维、上手四个口径分开比
性能:不要把 JWT 默认当成更快。Session 每次请求多一次存储查询,JWT 每次请求多一次验签计算,开销都取决于实现和存储延迟。量法是压同一个受保护接口,例如用 wrk 打 30 秒,同时在应用日志里打出鉴权中间件耗时;先量再改,别凭印象替换。
成本:Session 的钱主要花在存储上。用 redis-cli info memory 看 used_memory_human,再按单个会话 key 的大小和预估会话数推算;云上实例价格去控制台实例详情页的计费项确认,价格会变,以页面当时显示为准。JWT 的增量主要在令牌体积带来的带宽,以及有吊销需求时的黑名单存储。
运维:Session 要盯 maxmemory-policy 和过期淘汰,CONFIG GET maxmemory-policy 能看到当前策略,用 allkeys-lru 把活跃会话淘汰掉是常见事故。JWT 要盯密钥轮换、吊销列表规模和签发方与校验方的时钟偏差,验签失败往往先怀疑这几项。
上手:Session 通常框架自带中间件,接入改动小;JWT 需要自己处理签发、刷新、吊销和算法选择,代码量不一定多,但边界要想清楚。评估时数一下需要改动的接口数量,而不是只看 demo 行数。
JWT和Session:什么情况选哪一边
- 后台管理系统、单体或少量实例、运营需要随时封禁某个账号:选 Session。前置条件是已有 Redis 或数据库能承载会话存储,并且 cookie 的 SameSite、Secure 配置与站点域名、协议匹配;否则先修配置再谈选型。
- Web、App、第三方开放接口共用一套鉴权,或服务间调用希望免查存储:选 JWT。前置条件是 access token 有效期压得足够短,refresh token 有单独的存储和吊销手段,并且校验端确实检查了 iss 与 aud。
- 已有 Session 又要对外提供接口:走混合,页面走 Session,开放接口走 JWT。前置条件是两条校验链路不要互相穿透,否则一个已被吊销的会话可能从另一条路径继续访问。改完后用同一账号在两个入口各登出一次,分别请求受保护接口确认都失效。