使用jose库签名JWT后无法加密完整签名JWT的技术求助
解决jose库先签名再加密JWT的问题
错误原因
控制台报错TypeError: JWT Claims Set MUST be an object,是因为你把签名后的JWT字符串直接传给了EncryptJWT的payload参数,但EncryptJWT要求传入的是JSON对象类型的Claims集合,而非已签名的JWT字符串。
解决方案
要实现先签名再加密的流程,需将已签名的JWT作为嵌套内容包装成一个对象,再传给EncryptJWT处理。具体步骤如下:
1. 调整调用逻辑,先签名再包装加密
先调用你的签名函数得到已签名的JWT字符串,再将其封装为对象传入加密函数:
// 1. 生成签名后的JWT const originalPayload = { userId: 123, username: "test" }; const signedJwt = await _encrypt(originalPayload); // 2. 将签名JWT包装成对象,传入加密函数 const encryptedJwt = await encrypt({ payload: { nestedJwt: signedJwt }, maxAge: 3600 // 加密后的有效期,按需调整 });
2. (可选)优化加密函数参数类型
如果想让加密函数更明确支持嵌套JWT的场景,可以修改参数定义:
async function encrypt({ nestedJwt, maxAge, }: { nestedJwt: string; maxAge: number; }): Promise<string> { const secret: Uint8Array = randomBytes(32); const salt = randomBytes(16); const encryptionSecret: Uint8Array = await hkdf( "sha256", secret, salt, "Generated Encryption Key", 32 ); // 直接把已签名JWT包装为对象作为加密payload return new EncryptJWT({ nestedJwt }) .setProtectedHeader({ alg, enc }) .setIssuedAt() .setExpirationTime(now() + maxAge) .setJti(crypto.randomUUID()) .encrypt(encryptionSecret); }
3. 解密与验证流程
拿到加密后的JWT后,需要先解密取出嵌套的签名JWT,再验证签名:
// 解密加密后的JWT const decryptedResult = await jwtDecrypt(encryptedJwt, encryptionSecret); // 取出嵌套的签名JWT const nestedSignedJwt = decryptedResult.payload.nestedJwt; // 验证签名并获取原始payload const verifiedResult = await jwtVerify(nestedSignedJwt, key); console.log("原始payload:", verifiedResult.payload);
注意事项
- 加密所用的
encryptionSecret和salt在实际场景中需妥善存储,否则无法解密;示例中临时生成的方式仅用于演示。 - 签名和加密的算法可根据需求调整,但需确保jose库支持对应算法组合。
内容的提问来源于stack exchange,提问作者Henrik
相关产品推荐
相关产品推荐

