Deno中Node.js crypto.sign的等价实现及代码迁移方案
迁移Google Access Token生成代码到Deno的解决方案
核心差异处理
Deno基于Web标准API,替代Node.js的Buffer和crypto.sign需要以下针对性调整:
替换
Buffer.from
Deno中使用TextEncoder将字符串转为Uint8Array,完全等效Node.js的Buffer.from功能。替换
crypto.sign
改用Deno内置的Web Crypto API(crypto.subtle)实现RS256签名,需先将PEM格式私钥导入为CryptoKey对象。调整Base64URL编码
基于Web API的btoa实现,处理字符替换与末尾等号去除逻辑。
完整Deno代码实现
// 读取环境变量 const clientEmail = Deno.env.get("CLIENT_EMAIL")!; const privateKeyPem = Deno.env.get("PRIVATE_KEY")!; // Base64URL编码实现 function base64urlEncode(data: Uint8Array | string): string { let base64: string; if (typeof data === "string") { base64 = btoa(unescape(encodeURIComponent(data))); } else { base64 = btoa(String.fromCharCode(...data)); } return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } // 导入PEM格式私钥为CryptoKey async function importPrivateKey(pem: string): Promise<CryptoKey> { // 去除PEM头部尾部并解码Base64 const pemHeader = "-----BEGIN PRIVATE KEY-----"; const pemFooter = "-----END PRIVATE KEY-----"; const pemContents = pem.replace(pemHeader, "").replace(pemFooter, "").replace(/\s/g, ""); const binaryDer = Uint8Array.from(atob(pemContents), c => c.charCodeAt(0)); return crypto.subtle.importKey( "pkcs8", binaryDer, { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["sign"] ); } // 创建JWT async function createJWT(privateKey: CryptoKey): Promise<string> { const iat = Math.floor(Date.now() / 1000); const exp = iat + 3600; // 有效期1小时 const header = JSON.stringify({ alg: "RS256", typ: "JWT" }); const claimSet = JSON.stringify({ iss: clientEmail, scope: "https://www.googleapis.com/auth/firebase.messaging", aud: "https://oauth2.googleapis.com/token", exp, iat }); // 构造未签名的JWT部分 const unsignedJWT = `${base64urlEncode(header)}.${base64urlEncode(claimSet)}`; // 将字符串转为Uint8Array用于签名 const unsignedBytes = new TextEncoder().encode(unsignedJWT); // 生成签名 const signature = await crypto.subtle.sign( "RSASSA-PKCS1-v1_5", privateKey, unsignedBytes ); // 签名转Base64URL const signatureBase64Url = base64urlEncode(new Uint8Array(signature)); return `${unsignedJWT}.${signatureBase64Url}`; } // 获取Google Access Token async function getAccessToken(): Promise<string> { const privateKey = await importPrivateKey(privateKeyPem); const jwt = await createJWT(privateKey); const response = await fetch("https://oauth2.googleapis.com/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", assertion: jwt }) }); const data = await response.json(); return data.access_token; } // 使用示例 // getAccessToken().then(token => console.log("Access Token:", token));
关键说明
- 环境变量:确保
CLIENT_EMAIL和PRIVATE_KEY已正确设置,私钥保持原PEM格式即可,无需额外转换。 - 异步处理:Web Crypto API的
importKey和sign均为异步操作,因此相关函数需使用async/await语法。 - 编码兼容:处理字符串时先转URI编码,避免Unicode字符导致的Base64编码异常。
内容的提问来源于stack exchange,提问作者DanMossa
相关产品推荐
相关产品推荐

