JWT字符串严格按Header.Payload.Signature三段式结构以点号分隔,Payload为中间段Base64Url编码的JSON;C++需手动两次find定位两点位置提取,再经字符替换与填充补全后Base64解码,所得为UTF-8字节流而非字符串,且必须验签才可信。

JWT字符串的三段式结构必须手动分割
JWT由header.payload.signature三部分用点号(.)分隔,Payload是中间那段Base64Url编码的JSON。C++标准库不提供JWT解析,必须先按.切分字符串,再取第二段——不能依赖第三方库自动识别或校验,否则可能误把损坏/伪造的JWT当合法输入处理。
常见错误是直接find_first_of('.') + 1找第二段起始,但JWT可能含多余点(如签名里有base64填充);正确做法是用std::string::find两次,严格定位两个点的位置:
std::string jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
size_t first_dot = jwt.find('.');
size_t second_dot = jwt.find('.', first_dot + 1);
if (first_dot == std::string::npos || second_dot == std::string::npos) {
// 非法JWT格式,丢弃
}
std::string payload_b64 = jwt.substr(first_dot + 1, second_dot - first_dot - 1);Base64Url解码需替换字符再调用标准Base64解码
JWT用的是Base64Url编码(RFC 4648 §5),和标准Base64仅两处不同:+→-、/→_,且无填充字符=(但实际JWT常带1~2个=)。直接喂给通用Base64解码器会失败。
必须先做字符替换,再补足填充:
立即学习“C++免费学习笔记(深入)”;
- 将
payload_b64中所有-替换成+,所有_替换成/ - 计算缺失的填充数:
(4 - payload_b64.length() % 4) % 4,然后追加对应数量的= - 再传给Base64解码函数(如OpenSSL的
BIO_f_base64(),或自己写的轻量实现)
漏掉替换或填充,解码后字节流会错位,后续JSON解析必然崩溃。
解码后的Payload是JSON字节流,不是字符串
Base64Url解码得到的是原始UTF-8字节序列,它恰好是JSON格式,但C++里没有“JSON字符串”类型——你拿到的是std::vector<uint8_t>或std::string(内容为二进制JSON,可能含\0)。若直接用c_str()传给JSON库,遇到嵌入的\0会被截断。
正确传递方式取决于所用JSON库:
- 用
nlohmann/json:直接构造json::parse(payload_bytes.begin(), payload_bytes.end()) - 用
rapidjson:用ParseInsitu并传入&payload_bytes[0]和长度 - 手写简单解析(如只取
exp字段):遍历字节找"exp"键,跳过引号和空格,用std::strtoll解析数字值
忽略字节流本质,硬转std::string再用.c_str(),是线上服务解析JWT时最隐蔽的崩溃源。
不要验证Signature就信任Payload内容
提取Payload只是第一步,不代表它可信。JWT的Signature才是防篡改关键。如果业务逻辑依赖exp、iss等字段,必须用密钥重新计算签名并比对——否则攻击者可修改Payload后重签名(若用HS256且密钥泄露),或直接删掉Signature伪造“无签名JWT”。
典型疏忽:
- 开发环境跳过Signature验证,上线后忘记打开
- 用
RS256却拿公钥去“验证签名”,实际应验签而非加密 - 验签前没检查Header里的
alg字段,被攻击者改成none绕过
提取Payload本身很快,但真正安全的使用,永远卡在密钥管理、算法匹配和验签流程上。


















