JWT 实际应用
广州飞翔科技(learnto.cn)技术团队内部教程 | 作者:小崔(后端开发)审核:白歌(架构师)
JWT 不仅是理论上的令牌格式,更是现代分布式系统中身份验证与授权的实用工具。本文结合飞翔科技的真实业务场景,讲解无状态会话、安全注意事项、Access/Refresh Token、OAuth2、OpenID Connect 以及单点登录(SSO, Single Sign-On)的实践方案。
无状态会话:把状态"搬"到客户端
传统 Session vs JWT 无状态会话
传统 Web 应用把用户会话存在服务器内存或 Redis 中,每次请求都要查一次会话表。当飞翔科技的用户量从 1 万涨到 100 万时,Session 存储成了瓶颈。
JWT 无状态方案 :服务器签发一个带签名的 JWT 给客户端,客户端每次请求带上来,服务器只需验证签名即可识别用户,无需查库。
购物车示例
飞翔科技场景 :孔蓝(产品经理)要求乐途商城支持"未登录加购物车"。小崔的方案是:用户未登录时,把购物车数据存在 JWT Payload 的
cart声明里,由前端保存在 Cookie 中。用户登录后,服务端把匿名购物车合并到用户账户。{ "sub": "anon_8f3a2b", "cart": [ {"sku": "FX-10086", "qty": 2, "price": 188.88} ], "iat": 1516239022, "exp": 1516242622 }黄俪(前端开发)问:"JWT 放在 Cookie 里不会被篡改吗?"小崔答:"有 HS256 签名呢,改一个字节签名就通不过验证。"
安全注意事项
签名剥离(Signature Stripping)
攻击者可能截断 JWT 的签名部分,把 header.payload.signature 变成 header.payload.,然后伪造一个 alg: none 的头部。服务端如果盲目信任头部声明的算法,就会跳过验证。
防御 :服务端必须 显式指定 允许的算法,绝不依赖客户端传来的 alg 字段。
CSRF:跨站请求伪造
JWT 如果存在 Cookie 中,且 Cookie 自动随请求发送,攻击者可以构造恶意网站诱导用户发起已认证请求。
防御 :
- 使用
SameSite=Strict或SameSite=LaxCookie 属性 - 关键操作增加二次验证(短信验证码、支付密码)
XSS:跨站脚本攻击
如果 JWT 存在 localStorage 中,XSS 脚本可以直接读取并外泄令牌。
防御 :
- 对前端输出做严格转义
- 设置合理的
exp(过期时间),让被盗令牌尽快失效 - 敏感操作使用短时效 Access Token + Refresh Token 机制
飞翔科技场景 :李眉(运维)在 WAF 日志中发现可疑请求,白歌(架构师)排查后发现是乐途论坛的富文本编辑器过滤不严,存在 XSS。幸好乐途商城的 JWT 设置了 15 分钟过期,攻击者窃取的令牌在被发现前就已失效。
Access Token 与 Refresh Token
为什么需要两种令牌?
- Access Token(访问令牌) :用于访问受保护资源,有效期短(如 15 分钟),被盗后损失窗口小
- Refresh Token(刷新令牌) :用于在 Access Token 过期后获取新的 Access Token,有效期长(如 7 天),只与授权服务器交互
飞翔科技场景 :杨英(活动运营)策划"双 11 秒杀",预计 QPS 会翻 10 倍。白歌把 Access Token 有效期从 30 分钟缩到 5 分钟,Refresh Token 保持 2 小时。"Access Token 越短,Redis 里黑名单越少,"白歌解释,"高并发下验证签名比查黑名单快得多。"
JWT 与 OAuth2
OAuth2 的令牌可以是 JWT
OAuth2 规范本身不强制令牌格式,但实践中 自包含 JWT 作为 Access Token 越来越流行。资源服务器可以直接验证 JWT 签名,无需每次向授权服务器 introspection(令牌内省)。
{
"sub": "user_10086",
"scope": "read:order write:cart",
"client_id": "feixiang_app",
"iss": "https://auth.learnto.cn",
"exp": 1516242622
}
飞翔科技场景 :飞翔科技开放 API 平台给第三方开发者。赵鸣(内容运营)对接了一个外部内容聚合 App。小崔给第三方签发 RS256 签名的 JWT Access Token,第三方用飞翔科技公开的公钥就能本地验证,不用每次请求都打到授权中心。
JWT 与 OpenID Connect
ID Token 是什么?
OpenID Connect (OIDC)在 OAuth2 之上增加了身份层,核心就是 ID Token——一个包含用户身份信息的 JWT。它告诉客户端"这个用户是谁",而不是"这个用户能做什么"。
典型 ID Token 载荷:
{
"iss": "https://auth.learnto.cn",
"sub": "user_10086",
"aud": "feixiang_mall_app",
"exp": 1516242622,
"iat": 1516239022,
"name": "大翔",
"email": "daxiang@learnto.cn"
}
三种授权流程
| 流程 | 适用场景 | JWT 使用方式 |
|---|---|---|
| 授权码流程(Authorization Code) | 服务端 Web 应用 | 后端用授权码换 Access Token + ID Token |
| 隐式流程(Implicit) | 纯前端 SPA | 直接返回 Access Token,ID Token 可选 |
| 混合流程(Hybrid) | 需要前端立即可用 ID Token | 授权码 + 部分 Token 前端直接获取 |
飞翔科技场景 :飞翔科技有多个子系统——乐途商城(SPA)、乐途管理后台(Web)、乐途 App(移动端)。白歌统一用 OIDC 授权码流程做登录:用户先到
auth.learnto.cn认证,拿到授权码后各子系统后端各自换 Token。"隐式流程把 Token 暴露在 URL 里,"白歌强调,"咱们所有生产系统一律用授权码流程。"
联邦身份与单点登录(SSO)
飞翔科技的 SSO 架构
飞翔科技旗下有乐途商城、乐途社区、乐途办公三大子系统。用户希望登录一次,处处通行。
方案设计 :
- 所有子系统信任同一个 身份提供者 (IdP, Identity Provider)——乐途认证中心
- 用户访问任意子系统时,未登录则重定向到认证中心
- 认证中心签发包含
iss、aud、sub的 JWT ID Token - 子系统用共享的公钥验证 JWT,建立本地会话
飞翔科技场景 :孙鹤(产品助理)提出:"能不能让用户在乐途商城登录后,打开乐途社区自动登录?"白歌据此设计了上述 SSO 方案。上线后朱璐(社群运营)反馈用户投诉"反复登录"的工单下降了 87%。
SSO 中的安全要点
- aud 声明 :每个子系统验证
aud是否匹配自己的客户端 ID,防止令牌被拿到其他子系统冒用 - exp 与 iat :严格校验过期时间和签发时间,拒绝时钟偏差过大的令牌
- HTTPS 全链路 :授权码和 Token 传输必须走 TLS,防止中间人截获
结语
JWT 的实际应用远不止"发一个令牌、验一个签名"。从乐途商城的购物车到跨子系统的 SSO,从 Access/Refresh Token 的双层设计到 OAuth2/OIDC 的标准化流程,JWT 是现代身份架构的粘合剂。理解这些场景,才能在架构设计时选对方案、守好安全。