如何使用 npm Jose 创建签名的 JWT,然后验证此令牌?

tre*_*mho 5 node.js jwt jose

我正在努力了解如何使用 npm jose 模块(https://www.npmjs.com/package/jose)在我的 Node 应用程序中创建和验证签名的 JWT 令牌。我的场景是这样的:我想签署经过身份验证的请求来访问资源。我可以成功为此请求授予令牌创建 JWT 声明,该声明尊重 \xe2\x80\x9cits\xe2\x80\x9d 和 \xe2\x80\x9caud\xe2\x80\x9d、\xe2\x80\x9cexp\ 的属性xe2\x80\x9d 等,但我想对其进行签名(即使用 SignJWT 对象和 \xe2\x80\x98sign\xe2\x80\x99 方法),以便当它作为请求我可以验证它并授予或拒绝访问。\n\n\xe2\x80\x9csign\xe2\x80\x9d 方法不\xe2\x80\x99t 似乎喜欢我为 \xe2\x80\x98key\xe2 传递的任何内容\x80\x99 参数(我没有传递任何选项 \xe2\x80\x94 也许我应该传递,但是什么?)。\n我正在尝试使用 RSA 密钥对。我想用私钥签名并用公钥验证。为了满足我的迫切需要,我想我可以使用对称密钥,但我正在考虑其他一些未来场景,在这些场景中我将需要证书密钥的这种经典 PKCS 关系。无论如何,我不\xe2\x80\x99认为这个选择与当前我的进步的障碍有任何关系。\n我首先尝试使用 jose/util/generate_key_pair 创建我的公共/私有对。但是当我去使用密钥时,错误告诉我我的实现不支持这一点。因此,我转而尝试在我的应用程序之外创建 \xe2\x80\x98pem\xe2\x80\x99 证书并应用它(作为文本),但这也失败了。\xe2\x80\x98sign\xe2\x80\x99 方法报告密钥必须是 \xe2\x80\x98KeyLike\xe2\x80\x99、\xe2\x80\x98CryptoKey\xe2\x80\x99 或 \xe2 \x80\x98Uint8Array\xe2\x80\x99 类型。好吧,UInt8Array(节点缓冲区)没有足够的类型信息:它不\xe2\x80\x99t说明该缓冲区中的内容,而\xe2\x80\x9cKeyLike\xe2\x80\x9d是一个如此模糊的定义,它\xe2\x80\x99s 可以忽略。在请求搜索引擎的神谕之后,我发现我可以使用 Node API 中的以下内容创建 CryptoKey 格式的密钥对:

\n
crypto.webcrypto.subtle.generateKey(\n    {\n        name: \'RSASSA-PKCS1-v1_5\',\n        modulusLength: 2048,\n        publicExponent: new Uint8Array([1, 0, 1]),\n        hash: "SHA-256"\n    },\n    true,\n    [\xe2\x80\x98sign\xe2\x80\x99, \xe2\x80\x98verify\xe2\x80\x99]\n).then((pair:any) => {\n    serverInstance.keyPair = pair\n})\n
Run Code Online (Sandbox Code Playgroud)\n

但是,当我到达签名部分时:\nsiaToken.sign(serverInstance.keyPair.privateKey).then(signature => {

\n

我收到一个异常,报告\n\xe2\x80\x9cTypeError: CryptoKey 不支持此操作\xe2\x80\x9d

\n

认为这可能与generateKey的\xe2\x80\x98usages\xe2\x80\x99参数有关,我在那里尝试了各种值,但没有成功。

\n

所以,我很困惑。谁能告诉我如何 (a) 最好地为此目的生成一对密钥以及 (b) 如何将这些密钥应用于 JWT 签名?

\n

nle*_*ern 6

我也在使用 JWT 进行签名和验证方面遇到了困难,jose但最终能够通过 HS256 对称密钥加密取得成功。我通过以下步骤生成了它(我jose-node-cjs-runtime仅用于 Node.js 用例。请随意替换为所需的包。另请注意,我发现这些代码适用于 Node.js 版本 16.7.0、16.9.0所以请确保安装其中任何一个。如果要将这些更改部署到生产环境,那么还必须确保部署环境具有相同的 Node.js 版本。实现此目的的一种方法是提及 Node.jsengines键入 中的版本package.json

添加所需的导入

// library for generating symmetric key for jwt
const { createSecretKey } = require('crypto');
// library for signing jwt
const { SignJWT } = require('jose-node-cjs-runtime/jwt/sign');
// library for verifying jwt
const { jwtVerify } = require('jose-node-cjs-runtime/jwt/verify');
Run Code Online (Sandbox Code Playgroud)

创建类型的密钥KeyObject

KeyObjectNode.js 建议在生成对称、非对称密钥时使用。使用以下代码生成并存储KeyObject中类型的对称密钥对象secretKey

const secretKey = createSecretKey(process.env.JWT_SECRET, 'utf-8');
Run Code Online (Sandbox Code Playgroud)

替换process.env.JWT_SECRET为足够长的字符串。它需要足够长(使用长度至少为 32 的字符串),否则在签署 JWT 时会抛出以下错误:HS256 要求对称密钥为 256 位或更大

签署 JWT

(async () => {
  const token = await new SignJWT({ id: '12345' }) // details to  encode in the token
      .setProtectedHeader({ alg: 'HS256' }) // algorithm
      .setIssuedAt()
      .setIssuer(process.env.JWT_ISSUER) // issuer
      .setAudience(process.env.JWT_AUDIENCE) // audience
      .setExpirationTime(process.env.JWT_EXPIRATION_TIME) // token expiration time, e.g., "1 day"
      .sign(secretKey); // secretKey generated from previous step
  console.log(token); // log token to console
})();
Run Code Online (Sandbox Code Playgroud)

验证 JWT

secretKey我们也将使用存储的相同对称密钥进行验证。以下代码可用于从请求标头(在 Express 应用程序中)提取令牌并验证令牌:

(async () => {
    // extract token from request
    const token = req.header('Authorization').replace('Bearer ', '');
    try {
      // verify token
      const { payload, protectedHeader } = await jwtVerify(token, secretKey, {
        issuer: process.env.JWT_ISSUER, // issuer
        audience: process.env.JWT_AUDIENCE, // audience
      });
      // log values to console
      console.log(payload);
      console.log(protectedHeader);
    } catch (e) {
      // token verification failed
      console.log("Token is invalid");
    }
})();
Run Code Online (Sandbox Code Playgroud)