[测试技术] 深入理解 JSON Web Token (JWT)

*原创内容,未获授权禁止转载、转发、抄袭。

在现代 Web 开发中,身份认证(Authentication)是绕不开的话题。随着微服务架构的流行和前后端分离模式的普及,传统的基于 Session-Cookie 的认证方式逐渐显露出其局限性。

取而代之的,是 JSON Web Token (JWT)。它轻量、无状态、跨语言,成为了目前最流行的跨域认证解决方案。

作为一名开发者,你可能已经在使用 JWT,但你真的理解它的内部原理吗?你知道如何安全地存储它吗?本文将带你从头到尾彻底解析 JWT。


1. 为什么我们需要 JWT?

在讲“是什么”之前,先看“为什么”。

传统的 Session 认证模式

在 Web 1.0 时代,我们通常这样做:

  1. 用户登录,服务器验证通过。
  2. 服务器在内存或数据库中创建一个 Session,并记录用户信息。
  3. 服务器将 Session ID 返回给浏览器,写入 Cookie
  4. 之后浏览器的每次请求都会自动带上这个 Cookie。
  5. 服务器拿着 Cookie 里的 ID 去查 Session,确认用户身份。

这种模式的问题在于:

  • 扩展性差(Stateful): 服务器必须保存状态。如果你的应用做负载均衡(Load Balancer),用户第一次请求到了服务器 A,第二次请求到了服务器 B,服务器 B 没有这个 Session,用户就掉线了(除非做复杂的 Session 同步或使用 Redis 集中存储)。
  • CORS(跨域)问题: Cookie 在跨域场景下处理起来非常麻烦。
  • CSRF 攻击: 基于 Cookie 的自动发送特性,容易遭受跨站请求伪造攻击。

JWT 的无状态(Stateless)革命

JWT 的核心思想是:服务器不再保存任何 Session 数据。

服务器仅负责生成一个“令牌”(Token),这个令牌里包含了用户是谁、有什么权限、什么时候过期。服务器对这个令牌进行签名(防止篡改),然后发给客户端。客户端自己保存这个令牌,每次请求带上即可。


2. JWT 是什么?长什么样?

根据 RFC 7519 标准,JWT 是一个紧凑的、URL 安全的字符串。它的样子通常是这样的:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ. SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

仔细观察,你会发现它由三部分组成,中间用点(.)隔开:

  1. Header(头部)
  2. Payload(负载)
  3. Signature(签名)

也就是:Header.Payload.Signature

第一部分:Header

Header 是一个 JSON 对象,描述了 JWT 的元数据,通常包含两部分:令牌类型(即 JWT)和使用的签名算法(如 HMAC SHA256 或 RSA)。

{ "alg": "HS256", "typ": "JWT" }

最后将此 JSON 进行 Base64Url 编码,就得到了第一部分。

第二部分:Payload

Payload 也是一个 JSON 对象,用来存放实际需要传递的数据。这些数据被称为 Claims(声明)

RFC 标准定义了一些由系统保留的声明(建议使用,但不强制):

  • iss (issuer): 签发人
  • exp (expiration time): 过期时间
  • sub (subject): 主题(通常是用户 ID)
  • iat (issued at): 签发时间

你也可以定义私有声明:

{ "sub": "1234567890", "name": "John Doe", "admin": true }

注意: Payload 也是经过 Base64Url 编码的,它是明文的! 任何人拿到 Token 都可以解码看到 Payload 的内容。千万不要在 Payload 中存放密码等敏感信息。

第三部分:Signature

这是最关键的部分,用于验证消息在传递过程中没有被篡改。

生成公式如下:

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), your-256-bit-secret )

服务器拿到 Header 和 Payload,用同样的算法和**只有服务器知道的密钥(Secret)**再算一次签名。如果算出来的签名和 Token 里携带的签名一致,说明这个 Token 是合法的,且内容没有被修改过。


3. JWT 的工作流程

  1. 用户登录:客户端向服务器发送用户名和密码。
  2. 生成 Token:服务器验证通过后,计算出 JWT(Header + Payload + Signature),返回给客户端。
  3. 存储 Token:客户端收到 JWT,将其存储在 LocalStorage 或 Cookie 中。
  4. 验证 Token:服务器拦截请求,提取 Token,验证签名是否正确、是否过期。
  5. 响应:验证通过,处理业务逻辑并返回数据;验证失败,返回 401 Unauthorized。

请求资源:客户端再次发起请求(如获取订单列表)时,通常会在 HTTP 请求头中携带 Token:

Authorization: Bearer <token>

4. JWT vs Session:优缺点深度对比

特性Session 模式JWT 模式
状态存储服务端(内存/Redis)客户端(自身存储)
扩展性较差(需同步 Session)极佳(天生支持分布式/微服务)
带宽消耗小(仅传输 Session ID)大(Header+Payload 越大 Token 越长)
安全性容易防御 XSS,需防 CSRF需防 XSS,天然免疫 CSRF
注销机制容易(服务端删除 Session 即可)困难(一旦签发,有效期内很难由服务端强制失效)

5. 必须掌握的 Security Best Practices(安全最佳实践)

很多 JWT 的安全漏洞并非技术本身的问题,而是使用姿势不对。作为专业开发者,以下几点必须牢记:

1. 永远使用 HTTPS

JWT 也就是一段字符串,如果在 HTTP 明文传输中被拦截,攻击者就可以冒充用户(中间人攻击)。全站 HTTPS 是标配。

2. 签名算法的选择

  • HS256 (对称加密):由于只有一个密钥(Secret),任何拥有该密钥的人都可以签发 Token。适用于单体应用。
  • RS256 (非对称加密):私钥签发,公钥验证。适用于微服务架构(认证中心发 Token,业务服务只验 Token)。

3. Token 存储位置:Cookie 还是 LocalStorage?

这是一个经典的争论。

  • LocalStorage:
    • 优点:使用方便,前端完全可控。
    • 缺点:容易受到 XSS(跨站脚本攻击)。如果黑客注入一段 JS 代码,就能轻松读出 localStorage 里的 Token。
  • HttpOnly Cookie:
    • 优点:JS 无法读取,免疫 XSS 盗取 Token。
    • 缺点:容易受到 CSRF 攻击(需要配合 CSRF Token 防御),且跨域稍微麻烦一点。

专家建议:如果安全性要求极高,建议存储在标记为 httpOnly 和 Secure 的 Cookie 中。

4. 解决“注销难”和“续期”问题

JWT 最大的痛点是:一旦签发,无法撤回。 如果用户修改了密码,或者管理员想封禁某个账号,旧的 Token 在过期前依然有效。

解决方案:

  • 黑名单机制:将已注销但未过期的 Token ID 存入 Redis(设置 TTL 与 Token 过期时间一致)。每次验证时查一下 Redis。虽然这牺牲了一点“无状态”的特性,但在安全和性能之间取得了平衡。
  • 双 Token 机制(Access Token + Refresh Token)
    • Access Token:有效期短(如 15 分钟),用于请求资源。
    • Refresh Token:有效期长(如 7 天),专门用于换取新的 Access Token。
    • 当 Access Token 过期,前端用 Refresh Token 去换新的。如果用户注销,服务端只需把 Refresh Token 设为无效,用户手中的 Access Token 最多活 15 分钟。

6. 总结

JWT 是现代 Web 应用架构(特别是前后端分离和微服务)中不可或缺的技术。

何时使用 JWT?

  • 微服务架构。
  • 移动端应用(App 无法像浏览器那样处理 Cookie)。
  • 不需要服务端保存过多状态的场景。

何时不使用 JWT?

  • 对安全性要求极高,需要极其严格的 Session 管理(如银行系统,通常还是用 Session)。
  • Token 数据量过大,导致 HTTP 请求头过重。

技术没有银弹,只有最适合的方案。希望这篇文章能帮你彻底搞懂 JWT,并在项目中优雅地使用它。

Read more

GLM-4v-9b实战指南:用llama.cpp GGUF格式在消费级GPU部署多模态模型

GLM-4v-9b实战指南:用llama.cpp GGUF格式在消费级GPU部署多模态模型 1. 为什么你需要关注GLM-4v-9b 你有没有遇到过这样的场景:一张密密麻麻的财务报表截图发到工作群,大家却没人愿意花十分钟手动抄录数据;或者客户发来一张手机拍的电路板照片,问“这个元件型号是什么”,你只能回个尴尬的微笑;又或者团队正在做竞品分析,需要从几十份PDF产品手册里快速提取图表信息——这些不是小问题,而是每天真实消耗工程师、运营、产品经理大量时间的“视觉理解黑洞”。 过去,这类任务要么靠人工硬啃,要么得调用API付费接口,响应慢、成本高、隐私难保障。直到2024年,智谱AI开源了glm-4v-9b——一个真正能在你自己的RTX 4090上跑起来的90亿参数多模态模型。它不只是一张“能看图说话”的新名片,而是把高分辨率图像理解能力,塞进了一张消费级显卡的显存里。 重点来了:它支持原生1120×1120输入,这意味着你不用再把一张A4扫描件缩成模糊小图上传;它对中文表格、小字号OCR、技术类图表的理解,在公开评测中直接超过了GPT-4-turbo和Claude 3 Opus;

使用 VS Code 与 GitHub Copilot 高效 Vibe Coding 指南

欢迎大家关注「几米宋」的微信公众号,公众号聚焦于云原生、AI、服务网格、工具教程、技术观察以及日常感悟等内容,更多精彩内容请访问个人网站 jimmysong.io。 📄 文章摘要 掌握 VS Code 与 GitHub Copilot 的高效开发技巧,提升你的编程体验与效率,开启愉快的 vibe coding 之旅。 🔗 在 jimmysong.io 上 阅读原文 体验更佳。 最近一段时间笔者试用了众多的 vibe coding(氛围编程)工具,但是试用了一圈后,最终还是选择了 VS Code 与 GitHub Copilot 的组合。不为别的,就是因为最得心应手、性价比最高、最有可扩展性。本文将从环境配置、工作空间和插件、界面布局、

[AI工具箱] Vheer:免费、免登录,一键解锁AI绘画、视频生成和智能编辑

[AI工具箱] Vheer:免费、免登录,一键解锁AI绘画、视频生成和智能编辑

项目简介 今天偶然发现了个堪称“赛博活佛”的AI网站,名叫Vheer。它的作风相当大方,里面绝大部分功能都直接免费敞开用,就问你服不服。 文生图、图生视频、智能修图这些主流AI功能一个不落。点开就能用。而且非常的大气,比如抠图,别的网站按张收费,它直接让你一口气传20张照片自动处理,完全免费,甚至你去花时间不需要注册。 它几乎移除了所有上手障碍。网站首页清晰地排列着各种功能,没有晦涩的术语。你想把文字变成图片,或者让静态照片动起来,点开对应的按钮,输入你的想法,结果很快就能呈现在你面前。整个过程简单得就像在用一款普通的手机APP。 食用指南 访问地址 传送地址 官网的免费会员上面写的几个非常吸引人的地方,第一没有任何水印,第二生成图片视频这些是没有任何数量上的限制,只有高级别的模型和高速通道不能使用(但是实测下来,生成的速度也是相当不错)。 网站也提供了一些订阅模式,可以使用更高级的模型,但是这些高级模型需要消耗算力点。根据自己的需要看是否订阅。 由于功能实在太多了,强烈建议亲手测试一下 操作与体验——文生图 官网光一个文生图的功能就折腾出来了40多个功能,除了

从GAN到ChatGPT:AIGC技术演进与实战应用指南

快速体验 在开始今天关于 从GAN到ChatGPT:AIGC技术演进与实战应用指南 的探讨之前,我想先分享一个最近让我觉得很有意思的全栈技术挑战。 我们常说 AI 是未来,但作为开发者,如何将大模型(LLM)真正落地为一个低延迟、可交互的实时系统,而不仅仅是调个 API? 这里有一个非常硬核的动手实验:基于火山引擎豆包大模型,从零搭建一个实时语音通话应用。它不是简单的问答,而是需要你亲手打通 ASR(语音识别)→ LLM(大脑思考)→ TTS(语音合成)的完整 WebSocket 链路。对于想要掌握 AI 原生应用架构的同学来说,这是个绝佳的练手项目。 从0到1构建生产级别应用,脱离Demo,点击打开 从0打造个人豆包实时通话AI动手实验 从GAN到ChatGPT:AIGC技术演进与实战应用指南 技术背景:关键模型演进时间轴 2014年 - GAN横空出世 生成对抗网络(GAN)通过生成器与判别器的对抗训练,首次实现了高质量图像生成。核心突破在于: