凌晨一点,办公室只剩两盏灯。
小刘盯着屏幕上的 401 报错,抓了抓头发:"杨工,用户登录之后,为什么每次请求还要带 Token?服务器不是已经记住他了吗?"
我端着咖啡走过去,扫了一眼他的代码:"你这是前后端分离,服务器没记住任何人。来,咱们从头捋一遍鉴权这几种方式,你以后面试和写博客都用得上。"
第一幕:Session-Cookie —— "我记住你了"
小刘:"最早的系统是怎么做的?"
我:"想象你去一家健身房办卡。"
小刘:"嗯。"
我:"你第一次去,前台登记了你的信息,给了你一张会员卡,卡号是8888。之后你每次去,只要报卡号,前台翻开登记本就查到了你的全部信息——姓名、剩余课时、会员等级。"
小刘:"所以卡号就是 SessionID?"
我:"对。浏览器就是会员,服务器就是前台。登录成功后,服务器创建 Session,把 SessionID 塞进 Cookie 返回。之后每次请求,浏览器自动带上 Cookie,服务器根据 SessionID 去查 Session 里的用户信息。"
小刘:"那为什么现在不都这么干?"
我:"三个问题。第一,服务器要存状态,用户量大了得靠 Redis 共享 Session;第二,跨域场景下 Cookie 不好用,App 也没 Cookie;第三,Cookie 配置不当容易被 CSRF 攻击。"
小刘:"所以 Session-Cookie 适合传统后台系统、企业内部系统,需要随时踢人下线的场景。"
我:"没错。"
第二幕:Token —— "你自己带着证明来"
小刘:"那 Token 呢?"
我:"还是健身房,但这次换了个模式。你第一次登记后,前台不给你卡号了,而是给你一张塑封的通行证,上面印着你的照片和有效期。之后你每次来,直接亮通行证,前台看一眼照片和日期,放行。"
小刘:"等等,通行证上印了信息,那服务器不用查登记本了?"
我:"不一定。Token 分两种。一种是不透明 Token,就像一串随机字符串abc123,服务器还是得去 Redis 里查它对应哪个用户;另一种是自包含 Token,信息直接写在 Token 里,服务器自己就能验证——这就是 JWT。"
小刘:"所以 Token 是个大类,JWT 是其中一种具体实现?"
我:"对。很多人说'JWT 和 Token',其实是把子类和父类并列了。"
第三幕:JWT —— "自带签名的通行证"
小刘:"JWT 到底长什么样?"
我在白板写下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEwMDEsImV4cCI6MTcwMDAwMDAwMH0.abc123signature我:"三段,用点号隔开:Header、Payload、Signature。"
小刘:"Header 我知道,说明算法和类型。Payload 放用户信息。Signature 是签名,防篡改。"
我:"对。登录成功后,服务器用密钥对 Header 和 Payload 做签名,生成 JWT 返回。客户端之后每次请求放在Authorization: Bearer xxx里。服务器拿到 JWT 后,用同一把密钥验签,只要签名对得上、没过期,就确认身份——不需要查库。"
小刘:"这太爽了,微服务之间只要共享密钥就能验签,不用每个服务都查 Redis。"
我:"但 JWT 有个致命弱点。"
小刘:"没法主动作废?"
我:"聪明。JWT 一旦签发,就像一张塑封通行证,除非等到期,否则你没法让它失效。用户改密码、被盗号、想强制下线,JWT 都拦不住。"
小刘:"那怎么办?"
我:"两个方案。一是短时效 access token + 长时效 refresh token,access token 两小时过期,refresh token 七天,过期了用 refresh token 换新的。二是JWT + Redis 黑名单,需要强制下线时把 JWT 的标识或版本号写入 Redis,验签通过后再查一次黑名单。"
小刘:"所以 JWT 不是银弹,得看场景。"
第四幕:OAuth2 —— "第三方授权"
小刘:"那 OAuth2 呢?它和 JWT 什么关系?"
我:"OAuth2 不是鉴权协议,是授权协议。它解决的是'让第三方应用访问我的资源,但不给它密码'的问题。"
小刘:"比如用微信登录?"
我:"对。你用微信登录知乎,知乎并不拿到你的微信密码,而是微信给知乎一个临时令牌,知乎用这个令牌去微信服务器换取你的头像和昵称。这个令牌可以是 JWT,也可以是普通 Token,OAuth2 不规定格式。"
小刘:"所以 OAuth2 是流程,JWT 是载体。"
我:"总结到位。"
第五幕:怎么选
小刘:"那实际项目里怎么选?"
我在白板上画了一张表:
| 方案 | 服务器存状态? | 核心优势 | 典型场景 |
|---|---|---|---|
| Session-Cookie | 存 | 可随时失效,实现简单 | 传统后台、企业内部系统 |
| 不透明 Token | 存(Redis) | 跨域友好,灵活控制 | 前后端分离、App |
| JWT | 通常不存 | 无状态,微服务友好 | 高并发、SSO、微服务 |
| OAuth2 | 协议层 | 第三方授权 | 微信登录、开放平台 |
我:"选型的判断逻辑很简单:需要随时踢人 → Session 或不透明 Token;微服务、高并发 → JWT;第三方登录 → OAuth2;大多数互联网项目 → JWT + refresh token,必要时加 Redis 黑名单兜底。"
尾声
小刘合上笔记本,长出一口气:"所以鉴权本质上就一个问题——服务器怎么确认'你是你',区别只是这个确认信息存在哪、怎么传、怎么验证。"
我点点头:"记住一句话:Session 是服务器记住你,Token 是你自己带着证明来,JWT 是这张证明自带防伪签名,OAuth2 是让别人替你说'他确实是他'。"
他笑了:"这句话我写进博客标题。"
窗外天快亮了,401 报错也消失了。但我知道,下一次他遇到 403 的时候,我们还得再聊一次——鉴权和授权,是两回事。
不过那是另一个故事了。