JWT合法验证必须执行完整校验链:先严格检查三段式结构及Base64URL编码合规性,再解码header和payload,最后验证签名、exp/iat等关键字段及算法白名单,缺一不可。

仅靠字符串拆分和 Base64 校验无法判断 JWT 是否合法——它可能语法正确但签名无效、过期或字段缺失。真正有效的验证必须解析 payload 并执行完整校验链。
先拆成三段并检查基本结构
JWT 必须严格满足 header.payload.signature 三段式,用 '.' 分隔,且每段都需是合法的 Base64URL 编码(非标准 Base64):
- 先用
s.find_first_of('.')和s.find_last_of('.')找到两个'.',确保恰好有两个,且位置不重叠、不为首尾 - 三段长度都不能为 0;任意一段为空(如
"a..c"或"a.b.")直接拒绝 - 每段需通过 Base64URL 字符集校验:只含
A–Z、a–z、0–9、'-'、'_',**不含'+'、'/'、'='**(这是 Base64URL 和 Base64 的关键区别) - 每段长度必须是 4 的倍数(Base64 编码规则),否则不是合法编码片段
Base64URL 解码 header 和 payload 前必须预处理
JWT 使用 Base64URL 编码,即把标准 Base64 的 '+' 替换为 '-'、'/' 替换为 '_',且**不带填充字符 '='**。很多开发者直接拿 std::base64_decode(或 OpenSSL 函数)去解,结果失败——因为它们默认按标准 Base64 处理:
- 手动将每段中的
'-'换成'+'、'_'换成'/',再补足'='到长度为 4 的倍数(例如"ab"→"ab==") - 或使用专为 JWT 设计的解码函数(如
jwt-cpp::base::decode或boost::beast::http::base64_decode,它们内置 Base64URL 支持) - 解码失败(返回空或抛异常)说明该段不是有效 Base64URL,整个 JWT 非法
payload 解析后必须校验关键字段与签名
即使 header 和 payload 能成功解码并解析为 JSON,也不代表 JWT 合法。常见漏判点:
立即学习“C++免费学习笔记(深入)”;
-
exp字段必须存在且为int64_t(非double),否则大时间戳会丢失精度;用json["exp"].get<int64_t>()</int64_t>显式取值,再与std::chrono::system_clock::now().time_since_epoch().count() / 1000000000比较 -
alg字段必须在白名单内(如"HS256"、"RS256"),且与你实际使用的验证算法一致;"none"必须显式拒绝 - 签名比对必须用恒定时间比较(如
CRYPTO_memcmp或openssl::HMAC_Final+ 自定义安全比对),禁用==或memcmp——否则存在时序攻击风险 - 不要跳过重新拼接步骤:必须用原始未解码的 header 和 payload 字节(
header_str + "." + payload_str)重新计算签名,而不是用解析后的 JSON 对象
别用正则或手写状态机代替真实解析
写个 R"(^[A-Za-z0-9_-]{3,}\.){2}[A-Za-z0-9_-]{3,}$)" 看似能筛出 JWT 样式字符串,但它完全无法识别:
- 非法 Base64URL 字符(如
"a.b.c+"中的'+') - header 或 payload 解码后不是合法 JSON(如
{"alg": "HS256", "typ": "JWT"缺少右括号) - 签名段长度异常(如超长导致内存溢出)或含 NUL 字节(
std::string允许,但多数解析器会截断)
真正需要的不是“长得像 JWT”,而是“能被安全验证”。只要涉及认证,就必须走完整解析 + 签名校验流程——省掉任何一环,都等于开放伪造入口。


















