什么是 JWT?理解三段结构与核心设计目标
JWT(JSON Web Token)是一种紧凑、URL 安全的令牌格式,由 RFC 7519 正式定义,用于在两方之间以 JSON 对象的形式安全地传递信息。
它的核心设计目标有三个:
- 自包含:Token 本身携带全部上下文信息,服务端无需查询数据库即可完成鉴权,非常适合水平扩展的分布式架构。
- 防篡改:通过数字签名(HMAC 或 RSA/ECDSA)保证 Header 与 Payload 在传输过程中没有被第三方修改。
- 易使用:三段结构 + Base64URL 编码,可直接放在 HTTP 请求头、URL 参数或 Cookie 中,跨语言与跨平台支持极佳。
一个完整的 JWT 由三段组成,段与段之间以点号(.)分隔:
- Header:声明签名算法(alg)、令牌类型(typ)与可选的 kid(key ID)等。
- Payload:承载业务声明(claims),如用户 ID、角色、权限、有效期等。
- Signature:对前两段进行签名的结果,用于验证完整性与来源。
典型外观(三段之间以点号分隔,以下每段一行展示):
三段都是 Base64URL 编码,任何获得 Token 的人都可解码读取内容——这一点请务必牢记,详见第 7 章。
在 我们的 JWT 工具 中,只需粘贴任意 Token,点击“解析/验证”就能立刻看到 Header 与 Payload 的原始 JSON,非常方便调试接口。
Header 与 Payload:关键字段与标准声明解析
Header 是 Token 的元信息区,最常见的字段有:
- alg:签名算法,常见值有 HS256、HS384、HS512、RS256、RS384、RS512、ES256、ES384、ES512、PS256 等;不安全值 none 应在生产环境中彻底拒绝。
- typ:令牌类型,标准值为 JWT,用来区分 JWT 与其他同类格式(JWS/JWE/JWK/JWKS)。
- kid:key ID,当服务端持有多把密钥(例如轮换场景)时,kid 用于指示本次签名应使用哪把公钥/密钥校验。
Payload 是 JWT 的"正文",其中可以自由放置业务字段。为了互操作性,RFC 7519 定义了一组标准声明:
- iss(issuer,签发者):标识是谁签发了这个 Token,用于多租户场景下区分来源。
- sub(subject,主题):标识 Token 所属的主体,通常填入用户 ID。
- aud(audience,受众):声明 Token 的预期接收方,服务端应校验自身是否匹配。
- exp(expiration,过期时间):Unix 时间戳,Token 在此时间之后失效。建议设置 15 分钟到 2 小时之间,过期越短越安全。
- nbf(not before,生效时间):Unix 时间戳,在此时间之前 Token 无效。
- iat(issued at,签发时间):Token 的签发时间。
- jti(JWT ID):Token 的唯一标识符,可用于实现一次性 Token 或黑名单机制。
实践建议:Payload 字段不建议超过 10 个;太多字段会让 Token 体积膨胀到数 KB,影响 HTTP Header 性能。同时切勿放入密码、身份证号、手机号、银行卡号等敏感信息,因为 Base64URL 不是加密。
在 在线工具 中,将 Payload 以 JSON 形式填写后点击“签名”即可获得完整 Token,可实时查看不同声明如何影响最终输出。
HS vs RS vs ES:三类算法的差异与选型建议
JWT 的签名算法主要有三大类,对应不同的密钥管理模型与安全强度。正确选型是架构层面最重要的决策之一。
- HS 系列(HS256 / HS384 / HS512):基于 HMAC 的对称签名,签名与校验使用相同的密钥。实现最简单、计算速度最快、跨语言库支持最好。缺点是任何持有密钥的人都可以签发有效 Token,因此密钥必须被严格控制,不适合分发到多个独立服务端。
- RS 系列(RS256 / RS384 / RS512):基于 RSA 的非对称签名,使用私钥签名、公钥验证。公钥可以公开分发到多个服务端,公钥泄露不会导致伪造风险。缺点是签名与校验的计算成本高于 HMAC,Token 体积也更大(典型 256 位密钥对应 342 字符签名)。
- ES 系列(ES256 / ES384 / ES512):基于椭圆曲线(ECDSA)的非对称签名,与 RS 系列类似,但签名更短(ES256 约 86 字符)、计算开销更低,适合移动端、IoT、边缘节点等资源受限环境。
选型建议:
- 服务端单体或内部系统 → HS256(简单、快速)
- 多服务端、OpenID Connect、OAuth2 授权服务器 → RS256(主流、公钥可公开分发)
- 移动端 / IoT / 边缘节点 / 带宽敏感 → ES256(更小、更快)
在 我们的 JWT 工具 中,可在同一界面切换 HS256/HS384/HS512/RS256/RS384/RS512/ES256/ES384/ES512,方便对比签名长度与格式差异。
7 个真实应用场景:JWT 可以解决哪些问题
JWT 几乎出现在每一个涉及"身份认证 + 水平扩展"的系统中。以下是 7 个典型应用场景:
- API 鉴权:前端在登录后获取 JWT,后续请求在 Authorization: Bearer {token} 头中携带,服务端校验签名与过期时间即可鉴权,无需查询 session。
- 微服务间认证:服务 A 调用服务 B 时,使用 HS256 或 RS256 签发一个短期 Token(例如 1 分钟),B 端仅校验签名与有效期,无状态、高性能。
- 单点登录(SSO):用户在一处登录后,JWT 可被多个子域或独立应用共同使用,只要各方都信任同一签发方的公钥/密钥。
- OpenID Connect(OIDC)/ OAuth2:现代身份协议(如 Auth0、Okta、Azure AD、Keycloak)默认使用 JWT 作为 ID Token 与 Access Token 的载体。
- 一次性 URL / 下载链接:将"用户有权下载某个文件 10 分钟"的声明编码到 JWT,服务端直接校验签名即可放行,不必维护临时状态。
- 移动端与 SPA 本地会话:React、Vue、Flutter、Swift 等前端技术栈都内置了 JWT 解析能力,可在客户端快速显示用户信息。
- Webhook 回调验证:某些平台会把请求摘要与元数据以 JWT 的形式附加在回调头中,接收方以公钥/密钥校验来源与完整性。
在上述场景中,使用 在线 JWT 工具 本地复现 Token 结构,可以快速判断"是客户端没带 Token、服务端校验逻辑有误、还是密钥/算法不匹配",极大缩短接口联调时间。
JWT vs Session Cookie vs Opaque Token:如何选择正确的方案
不同方案之间并非互相排斥,实际项目中常以组合方式出现。以下是三者的核心差异:
- 传统 Session Cookie:服务端在 session store(Redis/DB)中保存会话状态,客户端仅持有一个不可猜测的 session ID。优点是易于撤销(直接从存储删除);缺点是水平扩展需解决 session store 共享与性能瓶颈,跨域体验差。
- JWT(自包含):服务端不保存会话状态,仅校验签名与声明。优点是无状态、水平扩展简单、跨语言跨域友好;缺点是撤销不易、体积随字段增长、Payload 不加密无法藏敏感数据。
- Opaque Token(不透明令牌):客户端仅持有一个随机字符串,服务端需反查数据存储才能获取用户信息。典型代表:OAuth2 的 reference token。优点是撤销立即、Payload 不暴露;缺点是每次鉴权都要查表,依赖高可用存储。
选择建议:
- 服务单体、用户规模小 → Session Cookie
- 多服务、微服务、跨域 SSO、移动端 → JWT
- 金融级场景、需要即时撤销、不愿暴露任何用户信息 → Opaque Token + 服务端查表
折中方案:短生命周期 Access Token(JWT)+ 长生命周期 Refresh Token(随机字符串)。Access Token 用于鉴权,过期自动失效;Refresh Token 用于换发新 Access Token,可被服务端黑名单撤销。这是 OAuth2 与现代应用的主流做法。
如需验证生成的 JWT 是否与服务端期望一致,可在 我们的 JWT 工具 中输入同样的密钥/私钥与 Payload,对比签名是否匹配。
6 个常见排错技巧:签名失败与 Token 不被校验的典型原因
JWT 的公式虽然简单,但在实际联调中因微小差异导致签名失败的情况屡见不鲜。以下是 6 条实用排错技巧:
- 统一字符编码与 JSON 序列化:JWT 签名是对 base64url(header).base64url(payload) 做的签名。Header 与 Payload 的 JSON 序列化(键顺序、空格、换行)在两端必须完全一致。请使用同一库的默认序列化策略,不要手动拼字符串。
- 统一密钥形式:HS256 的密钥是一段字节(通常是字符串的 UTF-8 字节),部分服务端期望 Base64 解码后的字节;RS256/ES256 期望的是 PEM 格式密钥。类型不匹配是最常见的排障点。
- 检查 Header 中的 alg 是否被服务端允许:有些服务端仅接受特定算法(例如只允许 RS256),若 Token 中声明为 HS256 则直接拒绝。同时务必在服务端拒绝 alg=none。
- 校验时间与 NTP 同步:exp / nbf / iat 都是以秒为单位的 Unix 时间戳(注意不是毫秒)。客户端与服务端时间差 5 分钟以上会导致 Token 过期被误判,建议服务器开启 NTP 同步。
- 检查 kid 与公钥/密钥:多密钥场景下若 kid 与实际用于签名的密钥不对应,校验必然失败。请检查 JWKS(JSON Web Key Set)端点是否正确发布了对应密钥。
- 换行与空白处理:PEM 格式的公私钥对换行敏感,粘贴时不要漏掉头尾标记(-----BEGIN PUBLIC KEY----- 等),也不要带入多余空白。
使用 本地工具 以"相同输入 + 相同算法"反复生成,比对服务器期望结果,可以快速定位是"参数错误"还是"库不同"。
Token 传输与存储的安全最佳实践
签出一个合法 JWT 只是开始,真正的风险在传输与存储环节。以下是生产级 JWT 部署的安全建议:
- 始终通过 HTTPS 传输:避免 Token 在网络层面被中间人截获。对 Cookie 形式的 Token 启用 Secure、HttpOnly、SameSite=Strict(或 Lax)标志。
- 缩短 Token 有效期:Access Token 建议 15 分钟以内,搭配 Refresh Token 机制完成换发;Refresh Token 可设置数天至两周,并在服务端维护一个可快速撤销的白名单。
- 绝不把敏感信息放入 Payload:Base64URL 只是编码不是加密,任何人都能解码。若必须传递敏感数据,请改用 JWE(JSON Web Encryption)进行内容加密,或仅在 Payload 中放入非敏感的引用 ID。
- 使用足够长的密钥与高安全强度的算法:HS256 密钥至少 32 字节(256 位),推荐 64 字节以上;RS256 至少使用 2048 位 RSA;ES256 使用 P-256 曲线。
- 在服务端开启严格的算法白名单:绝不接受 alg=none;对 RS256 场景进行"算法与密钥类型"双重匹配,避免算法混淆攻击。
- 校验标准声明:至少校验 exp(过期)、iss(签发者)、aud(受众)三项,必要时校验 nbf。
- 不要在前端 localStorage 长期保存 Token:localStorage 无法防御 XSS,一旦脚本注入攻击者即可读取。若必须在前端保存,优先使用 HttpOnly Cookie,或至少缩短 Token 生命周期并在退出时主动清除。
- 密钥轮换与 kid 机制:为每把密钥赋予一个 kid,在 Token Header 中携带,服务端通过 JWKS 端点发布公钥。轮换时只需新增一把密钥,旧密钥在过渡期内仍可被识别与校验。
选择 一个本地处理、无日志、无上传的在线 JWT 工具 可以在不牺牲便利的前提下牢牢掌握密钥控制权。