CHAPTER 20
JWT 函数手册
依据源码:include/jwt.h、src/jwt.cpp。
0. 模块能力总览
JWT 提供三类能力:
- Token 生成/校验(HS256)
- Claim 解析与 TTL 处理
- 密码哈希/校验(PBKDF2 风格迭代 HMAC-SHA256)
关键成员:
secret_:签名密钥algorithm_:当前固定HS256defaultTTL_:默认过期秒数static JWT* instance_:单例指针
1. 构造与配置接口
1.1 JWT::JWT(const std::string& expiresIn, const std::string& jwtPrivateKeyPath)
- 作用:单例风格构造,允许通过 TTL 字符串与密钥文件初始化。
- 流程:
- 设
algorithm_=HS256,defaultTTL_=3600 - 若
instance_ != nullptr,仅打印"JWT instance already exists"并return - 若
expiresIn非空,调用parseTTL,结果 >0 则覆写defaultTTL_ - 若
jwtPrivateKeyPath非空,调用loadSecretFromFile instance_ = this
- 设
注意:这是“软单例”构造,重复构造不会抛异常,但行为可能让调用者误判(只打印错误)。
1.2 JWT::JWT(std::string secret, long long ttlSeconds)
- 作用:直接以密钥 + TTL 构造。
- 行为:
secret_ = secretdefaultTTL_ = (ttlSeconds > 0 ? ttlSeconds : 3600)- 若
secret_为空,抛invalid_argument
1.3 void setSecret(const std::string& secret)
- 非空校验,不通过则抛
invalid_argument - 成功后覆写
secret_
1.4 void loadSecretFromFile(const std::string& path)
- 以二进制打开文件
- 失败抛
runtime_error("Failed to open secret file: ...") - 读完整文件到字符串流,再调用
setSecret
1.5 void setDefaultTTL(long long ttlSeconds)
- 必须
> 0 - 否则抛
invalid_argument("TTL must be positive")
1.6 std::string getSecret() const / Algorithm algorithm() const
- 只读返回密钥副本与当前算法枚举
2. Token 生成与校验
2.1 std::string generateToken(const std::map<std::string,std::string>& customClaims, long long ttlSeconds) const
作用
生成 header.payload.signature 三段 JWT。
关键步骤
- 检查
secret_非空,否则抛runtime_error header = buildHeader(),再base64UrlEncode- 取当前
issuedAt(秒级时间戳) - 计算有效 TTL:
- 参数 < 0 -> 用
defaultTTL_ -
0 则设置
exp = iat + ttl - <=0 时可不带
exp
- 参数 < 0 -> 用
payload = buildPayload(customClaims, issuedAt, expiresAt)并编码signingInput = headerEncoded + "." + payloadEncodedsignature = base64UrlEncode(hmacSha256(signingInput, secret_))- 返回
signingInput + "." + signature
2.2 bool verifyToken(const std::string& token, std::string* payloadJson) const
作用
校验 token 格式、签名与过期时间。
关键步骤
- 若
secret_空,直接 false parts = splitToken(token),必须是 3 段- 重算签名并比对(当前是
expectedSignature != parts[2]) - 解码 payload:
base64UrlDecode(parts[1]) - 提取
exp:extractNumericClaim(payload, "exp") - 若有
exp且当前时间>exp,返回 false - 若
payloadJson非空,写回 payload 文本 - 返回 true
安全注意
- 当前签名比较使用普通字符串比较,不是常量时间比较。
- 模块虽实现了
constant_time_compare,但此处未使用。
2.3 std::optional<std::map<std::string,std::string>> parseClaims(const std::string& token) const
- 先
verifyToken(token, &payload),失败返回nullopt - 再
parseJsonObject(payload),异常则nullopt - 成功返回 claims map
3. 编码与签名基础函数
3.1 static std::string buildHeader()
- 固定返回:
{"alg":"HS256","typ":"JWT"}
3.2 static std::string base64UrlEncode(const std::string& data)
- 标准 base64 编码后:
+ -> -/ -> _- 去掉末尾
=
3.3 static std::string base64UrlDecode(const std::string& data)
- 先还原字符:
- -> +_ -> /
- 补齐
=到 4 的倍数 - 解码为原始字节串
3.4 static std::string sha256(const std::string& data)
- 自实现 SHA-256:
- 消息填充
- 512-bit 分块
- 64 轮压缩
- 输出 32 字节原始哈希
3.5 static std::string hmacSha256(const std::string& data, const std::string& key)
- 标准 HMAC 过程:
- key 按 blockSize=64 归一化
iKeyPad/oKeyPadsha256(oKeyPad + sha256(iKeyPad + data))
3.6 static inline uint32_t rotor(uint32_t value, uint32_t count)(匿名命名空间)
- SHA-256 内部循环右移工具函数
4. Claim 与 JSON 处理函数
4.1 static std::string buildPayload(...)
- 用
JsonValue组装 payload:- 必填
iat - 可选
exp - 追加
customClaims
- 必填
toJson()返回文本
4.2 static std::vector<std::string> splitToken(const std::string& token)
- 以
.分割,返回段数组
4.3 static std::optional<long long> extractNumericClaim(const std::string& payload, const std::string& claimKey)
- 在 payload 文本中查找
"claimKey": - 跳过空白,解析可选负号 + 数字
- 找不到/无数字返回
nullopt
4.4 static long long parseTTL(const std::string& ttlSpec)
- 支持单位:
s秒m分钟h小时d天
- 支持组合串(如
1h30m) - 解析失败返回 0
4.5 static std::map<std::string,std::string> parseJsonObject(const std::string& json)
- 用
JsonValue::fromJson解析 - 要求根类型必须是
OBJECT,否则抛异常 - 值类型支持:
- STRING -> 原字符串
- NUMBER/BOOLEAN ->
toJson() - NULL ->
"null" - OBJECT/ARRAY -> 抛异常(当前不支持嵌套对象值导出到 string map)
4.6 static std::string escapeJson(const std::string& input) / static std::string unescapeJson(const std::string& input)
escapeJson:手工转义控制字符与特殊字符unescapeJson:复用JsonValue::parseString执行反转义
4.7 static std::string getStringFromJsonValue(const JsonValue& value, const std::string& key)
- 若
value非字符串,抛runtime_error - 是字符串则返回
asString()
5. 密码哈希相关函数
5.1 static std::string encrypt_password(const std::string& password)
- 目标格式:
salt_hex:iterations:hash_hex - 固定迭代:
100000 - 流程:
salt = generate_salt()- 执行 PBKDF2 风格迭代(基于
hmacSha256) - 输出
bytes_to_hex(salt) + ":" + iterations + ":" + bytes_to_hex(dk)
注意:
generate_salt()本身已经返回十六进制字符串,这里再次bytes_to_hex(salt)会把“hex文本”再按字节转 hex,格式与常见实现不同但与当前verify_password协同一致。
5.2 static bool verify_password(const std::string& password, const std::string& stored_hash)
- 解析
salt_hex:iterations:hash_hex - 用
hex_to_bytes还原盐值与期望哈希 - 迭代次数必须为正整数
- 复算哈希后使用
constant_time_compare比较 - 成功 true,否则 false
5.3 static std::string generate_salt()
- 生成 16 字节随机数据
- 使用
std::random_device + mt19937_64 + uniform_int_distribution<uint8_t> - 返回十六进制字符串
5.4 static std::string bytes_to_hex(const std::string& bytes) / static std::string hex_to_bytes(const std::string& hex)
bytes_to_hex:字节 -> 2位hex文本hex_to_bytes:hex文本 -> 字节串(奇数长度返回空串)
5.5 static bool constant_time_compare(const std::string& a, const std::string& b)
- 长度不等直接 false
- 长度相等时逐字节异或累积
diff - 最终
diff == 0判等
6. 单例接口
static JWT* getInstance()
- 若
instance_ == nullptr,执行instance_ = new JWT(); - 返回
instance_
风险点
- 默认构造未保证
secret_已设置。 - 若调用方未再
setSecret或loadSecretFromFile,generateToken会抛错,verifyToken会直接 false。
7. 已声明但在本文件未见实现的函数
jwt.h 声明了:
sha256_bytes(const std::vector<uint8_t>& data)hmac_sha256(const std::vector<uint8_t>& key, const std::vector<uint8_t>& data)
在当前 src/jwt.cpp 中未看到对应定义。
若链接未报错,说明工程里未实际引用这两个符号;否则需补实现或移除声明。
8. 面试可直接讲的风险与改进
verifyToken签名比较建议改为常量时间比较。- 自研密码学组件建议引入成熟库并保留接口层。
- 单例初始化流程需要更严格(避免无 secret 的默认实例)。
- claims 解析目前只导出扁平字符串 map,嵌套对象值会报错,可扩展 JSON 值透传。
下一模块(顺序)
RDConnector → docs/modules/detail/RDConnector_FUNCTIONS.md