2、用户认证机制:Session vs JWT,Cookie与的安全存储
用户认证,说白了就是回答三个问题:你是谁?你怎么证明?我怎么信你?
我做了这么多年安全架构,见过太多团队在认证这块踩坑。有的项目上线第一天就被拖库,有的因为泄露导致数据被爬。今天咱们就把Session和JWT这两个主流方案掰开揉碎,看看它们到底怎么选、怎么存才安全。
2.1 Session vs JWT:两种认证哲学
先看一张对比图,帮你快速建立整体认知。
2.2 Session:服务端有状态
Session的原理很简单。用户登录成功后,服务端生成一个唯一ID,存到内存或Redis里。这个ID通过Cookie发给浏览器。下次请求时,浏览器自动带上Cookie,服务端查一下Session数据,就知道你是谁了。
我习惯把Session比作「酒店房卡」。房卡本身只是一串数字,真正的房间信息存在前台系统里。你拿着房卡去开门,前台先查系统确认你是302房的客人,才放你进去。
Session的优点
- 服务端完全可控:想踢人下线?删掉Session就行。我在项目中遇到过紧急情况,发现某个用户账号被盗,直接Redis里del掉他的Session,对方立即失效。
- Payload无大小限制:Session里存多少数据都行,反正不经过网络传输。
- 实现简单:主流框架都有现成支持,Spring Session、Express Session,开箱即用。
Session的痛点
- 扩展性差:多台服务器必须共享Session存储。要么用Redis集中管理,要么用粘性Session。我见过一个团队上线后才发现负载均衡没配Session共享,用户每次刷新都重新登录,那叫一个尴尬。
- 移动端不友好:App原生请求不会自动管理Cookie,需要手动处理。
- CSRF风险:Cookie自动携带的特性,容易被跨站请求伪造利用。
2.3 JWT:无状态的自包含令牌
JWT(JSON Web )的思路完全不同。它把用户信息加密签名后,直接发给客户端。服务端不再存任何会话数据。下次请求时,客户端把JWT带回来,服务端验签通过就直接解析出用户信息。
你想想看,这就像「身份证」。身份证上印着你的姓名、照片、有效期,任何机构只要验证防伪标识,就能确认你的身份,不需要打电话回公安局查。
JWT的结构
一个JWT由三部分组成,用点号分隔:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
| 部分 | 内容 | 说明 |
|---|---|---|
| Header | 签名算法、类型 | Base64编码,可解码查看 |
| Payload | 用户ID、角色、过期时间等 | Base64编码,不要放密码 |
| Signature | 对前两部分的签名 | 防止篡改,需要服务端密钥验证 |
JWT的优点
- 天然支持分布式:服务端不需要共享存储,任何节点只要知道密钥就能验证。
- 跨域友好:可以放在Authorization Header里,不受Cookie同源策略限制。
- 适合移动端和微服务:App和微服务之间传递JWT非常方便。
JWT的痛点
- 无法主动失效:JWT签发后,在过期之前一直有效。想踢人下线?除非维护黑名单,但那就又变成有状态了。
- Payload体积大:每次请求都要携带,如果存太多信息,会显著增加网络开销。
- 密钥管理复杂:签名密钥泄露等于全线失守。
2.4 Cookie与的安全存储
无论用Session还是JWT,最终都要把凭证存到客户端。怎么存才安全?这里面的坑可不少。
Cookie的安全属性
如果选择用Cookie存Session ID或JWT,以下四个属性必须配齐:
Set-Cookie: session_id=abc123;
HttpOnly; // 禁止JavaScript读取,防止XSS窃取
Secure; // 仅HTTPS传输
SameSite=Strict; // 禁止跨站请求携带,防止CSRF
Path=/; // 限定路径范围
Max-Age=3600 // 设置过期时间
的存储方案
如果JWT放在Authorization Header里传输,客户端怎么存?
| 存储位置 | 优点 | 风险 | 推荐场景 |
|---|---|---|---|
| localStorage | 持久化,刷新不丢失 | XSS可读取,无法HttpOnly | 不推荐 |
| sessionStorage | 关闭标签页自动清除 | XSS可读取,多标签不共享 | 低安全要求 |
| HttpOnly Cookie | XSS无法读取 | 需处理CSRF | 推荐(Web端) |
| 内存变量 | 最安全,不持久化 | 刷新页面丢失 | 高安全要求 |
我个人最推荐的做法是:JWT存HttpOnly Cookie + 额外CSRF 。这样既防XSS窃取,又防CSRF利用。移动端则用系统安全存储(iOS Keychain / Android EncryptedSharedPreferences)。
2.5 避坑指南
这些年我踩过的坑,列出来给大家提个醒:
- JWT过期时间别设太长:我曾经见过有人设30天,结果泄露后整整一个月控制不了。建议Access 15分钟,Refresh 7天。
- 不要在JWT里放敏感信息:Payload是明文,放手机号、邮箱都有风险。真要放,至少加密一下。
- Session存储别用默认内存:生产环境一定要用Redis,否则重启服务所有用户都要重新登录。
- Cookie的Domain别设太宽:设成.com会导致所有子域名共享Cookie,安全边界被打破。
- 密钥定期轮换:JWT签名密钥至少每季度换一次,换完后旧要能平滑过渡。
总结一下:
- 单体应用、需要服务端主动控制 → Session
- 分布式、微服务、移动端 → JWT
- 无论哪种,客户端存储都要用 HttpOnly + Secure + SameSite
- 永远不要信任客户端传来的任何数据,验签和校验是底线