JWT
JWT 是一种自包含的结构化 Token 格式;重点在签名、claims、验证边界,以及它与 Access Token、Refresh Token、Session 和 Authorization 的区别。
[!info] related notes
- 所属 MOC: Node.js 后端面试 MOC, 安全 MOC
- 前置关系: Cookie、Session、Token 与 JWT 的关系
- 凭证角色: Access Token, Refresh Token
- 状态模型: Server-side Session 鉴权
- 架构关系: Stateful、Stateless 与 Hybrid 认证架构
- Go 实践: Go 认证与 JWT, JWT 认证中间件设计
JWT
一句话定义
JWT(JSON Web Token)是一种把 claims 放进结构化载荷,并通过签名/MAC 保护完整性与签发关系的 Token 格式。
最重要的边界是:
JWT 是凭证格式,不是 Session,不是 Access Token 的同义词,也不是一整套认证架构。
结构
JWT 常见三段:
Header.Payload.Signature
Header:算法、类型等元数据;Payload:sub、exp、iat、iss、aud、scope 等 claims;Signature:验证 Header + Payload 是否被篡改,以及是否来自持有对应密钥的一方。
Payload 通常只是 Base64URL 编码,不是机密加密区。敏感秘密不应该因为“放进 JWT”就认为被隐藏。
签名不是加密
验证 JWT 通常是:
parse
+
verify signature
+
validate exp / nbf / iss / aud / allowed alg
而不是“解密 JWT”。
只有 JWE 等加密 Token 才涉及机密性;普通签名 JWT 主要解决完整性与来源验证。
JWT 能直接证明什么
签名和 claims 校验通过,可以支持:
- Token 在签发后未被篡改;
- Token 来自被信任的签发方;
- Token 仍在自身时间窗口内;
aud、iss、scope 等满足当前验证规则。
JWT 不能天然证明什么
它不能单独证明:
- 数据库用户现在仍存在;
- 用户没有刚被封禁;
- 某个长期 Session / Grant 仍然 live;
- 用户 Role 没有变化;
- 用户仍然拥有某个具体资源;
- Token 没有被盗取后重放。
因此:
JWT cryptographic validity
≠
current session liveness
≠
resource authorization
JWT 与 Access Token
Access Token 是“拿来访问 API 的凭证角色”;JWT 是“凭证格式”。
所以:
Access Token
→ 可以是 JWT
→ 也可以是 opaque token
短期 Access JWT 的一个重要价值是:Resource Server 可以本地验证签名和 claims,不必每次回 Auth Server 或 Session Store。
JWT 与 Refresh Token
Refresh Token 也可以做成 JWT,但完全没有必须这样做。
很多系统更倾向:
opaque random Refresh Token
→ 服务端保存 hash / family / session mapping
→ refresh 时检查 revoke / expiry / rotation
因为 Refresh Token 本质上需要很强的生命周期控制,而“它是否自包含”并不是最核心的问题。
两种 JWT + Session 组合不要混为一谈
模式 A:每请求都查 Session
JWT
→ verify
→ read session_id claim
→ lookup Session
→ allow / deny
优点是 Session revoke 可以即时生效;代价是每次请求都依赖 Session Store,因此 JWT 的无状态快路径价值较小。
模式 B:Hybrid 短期 Access JWT
普通 API:
Access JWT
→ local verify
→ 不查 Session Store
Refresh / Logout / Revoke:
Refresh Token
→ Auth Server
→ check Session / Grant
这种模式让长期生命周期有状态,而高频访问走短期无状态快路径。
如果 Session 在 Access JWT 签发后被撤销,已签发 Token 可能继续有效到 exp;短 TTL 的意义就是限制这个 stale window。
撤销是 JWT 的核心架构取舍
纯 Stateless JWT 一旦签发,在 exp 前很难精确立即撤销某一个 Token。
常见策略:
- 缩短 Access Token TTL;
- 使用 Refresh Token + Server-side Grant;
- blacklist / denylist;
- token version;
- 对敏感操作额外检查实时 Session/User 状态。
一旦每个请求都必须查询 revoke/session 状态,系统就不再是纯 Stateless;这不是错误,而是用实时控制换取额外状态依赖。
Claims 不是数据库实时真相
例如 JWT 中有:
{
"sub": "42",
"role": "admin",
"exp": 1234567890
}
这只表示签发时或签发方认可的 claims 快照。
如果 Role 在数据库中随后变更,而 Resource Server 不重新查询,那么 JWT 中的旧 Role 会存在到 Token 过期。
因此不要把长生命周期 JWT 当成长期业务状态同步机制。
验证安全边界
- 明确限制允许的
alg,不要盲信 Token Header; - 验证
exp/nbf等时间约束; - 按场景验证
iss和aud; - 密钥要有轮换策略;
- 不把敏感秘密放在可读 Payload;
- Bearer JWT 一旦泄露,在有效期内通常可被持有者使用。
常见误区
- “JWT 是加密字符串” → 常见 JWT 是签名,不是加密。
- “能 decode 就合法” → decode 只是解析,必须 verify。
- “JWT = Access Token” → JWT 是格式,Access Token 是角色。
- “Refresh Token 必须 JWT” → 不需要。
- “JWT 一定无状态” → 是否无状态取决于服务端是否查询 Session/revocation 状态。
- “JWT 验证成功就完成授权” → 资源级 Authorization 仍可能依赖数据库当前状态。
- “JWT 比 Session 更现代” → 不是同一层概念,不能这样比较。
最短记忆
JWT 是一个可签名验证的 claims 容器。Access/Refresh 决定 Token 做什么,Session 决定长期登录状态是否存在,Authorization 决定当前用户能不能操作当前资源。
学习检查
不看正文回答:
- JWT 是“认证方案”、Token 角色,还是一种结构化凭证格式?
- 普通签名 JWT 的 Payload 为什么不能当作机密数据区?
decode与verify的安全语义有什么根本不同?- 为什么 JWT 签名验证成功只能证明 claims 的完整性/签发关系,不能证明当前 Session 一定仍然有效?
- Access Token 与 Refresh Token 为什么都“可以是 JWT”,但不“必须是 JWT”?
- Pure Stateless JWT 的精确撤销为什么困难?常见补偿策略有哪些?
- 一个用户刚被封禁但旧 Access JWT 尚未过期时,系统是否仍可能接受它?这取决于什么架构选择?
掌握验收
- 能区分 JWT、Access Token、Refresh Token、Session、Authorization 五个层次;
- 能完整说明签名、claims 验证、撤销与 stale window;
- 能根据业务实时性要求选择纯 Stateless、每请求查 Session 或 Hybrid 路线。