ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Postman自动化接口测试:集成AES与国密SM4加解密实战指南

Postman自动化接口测试:集成AES与国密SM4加解密实战指南 1. 从手动复制粘贴到自动化为什么我们需要在Postman里处理加解密如果你经常和需要加解密的后端接口打交道下面这个场景你一定不陌生开发给了你一个接口文档上面写着“请求体需用SM4加密密钥是xxx模式是CBC填充是PKCS5Padding”。你拿到一个明文的JSON比如{idCard: 110101199001011234, name: 张三}然后你打开一个在线的加密工具网站或者启动一个本地的小脚本把明文、密钥、IV如果有填进去点击加密得到一串长长的密文。接着你把这串密文小心翼翼地复制到Postman的请求体里发送请求。如果后端返回“解密失败”你又得回到加密工具检查是不是模式选错了、IV没加、或者密钥填错了再来一轮复制粘贴。这个过程繁琐、低效且容易出错。当接口参数需要动态变化或者你需要用不同的测试数据批量验证时这种手动操作几乎是一场灾难。更麻烦的是有些接口的响应也是加密的你收到一堆乱码还得再手动解密才能看到真正的业务数据是否如预期。Postman作为API测试的瑞士军刀其核心价值在于自动化、可重复和协作。如果加解密这个环节被隔离在外部工具中整个测试流程就出现了严重的“断点”。因此将加解密逻辑内置于Postman实现请求的自动加密和响应的自动解密是提升接口测试效率和质量的关键一步。这不仅仅是省去了复制粘贴的步骤更是将加解密变成了测试用例的一部分使得参数化测试、数据驱动测试、以及CI/CD流水线中的接口自动化测试成为可能。今天我们就来深入探讨如何在Postman中借助Pre-request Script和Tests Script优雅地处理AES、SM3、SM4这些常见的加解密算法让我们的接口测试工作流真正流畅起来。2. 核心工具与原理Postman脚本、CryptoJS与国密算法库要在Postman中实现加解密我们主要依靠两个核心Postman内置的JavaScript执行环境以及强大的第三方加密库。理解这两者是如何工作的是后续一切操作的基础。2.1 Postman的脚本沙箱Pre-request与TestsPostman为每个请求提供了两个关键的脚本执行节点Pre-request Script请求前脚本在请求被发送之前运行。这是我们实现请求参数自动加密的主战场。你可以在这里获取环境变量、全局变量中的明文数据调用加密函数进行处理然后将结果动态设置到请求的URL、Params、Headers或Body中。Tests Script测试脚本在收到响应之后运行。传统上我们用它来做断言校验但它同样可以处理响应体的自动解密。你可以在这里访问pm.response.text()获取原始的可能是加密的响应文本调用解密函数进行处理然后将解密后的明文存入变量供后续的断言使用或者直接console.log输出查看。这两个脚本运行在一个加强了功能的Node.js-like沙箱环境中它内置了如CryptoJS这样的常用库但版本和功能有限也允许我们通过require方式引入外部库。2.2 加密库选型CryptoJS与sm-crypto对于不同的算法我们需要引入不同的库AES加密这是最方便的。Postman沙箱环境内置了CryptoJS库。你可以直接使用CryptoJS.AES.encrypt和CryptoJS.AES.decrypt方法。这个内置的CryptoJS版本通常足够处理AES的CBC、ECB等常见模式和PKCS5/PKCS7填充。这是我们的首选方案。SM2/SM3/SM4国密算法Postman环境没有内置国密算法支持。我们必须通过外部引入。目前最成熟、兼容性最好的选择是**sm-crypto**这个纯JavaScript实现的国密算法库。我们需要在脚本中通过require语句来加载它。这里有一个至关重要的技术细节Postman的脚本环境不支持直接从网络URL如CDN动态加载外部JS库。你不能写script src...。正确的做法是将目标库的完整源代码以字符串的形式复制粘贴到你的脚本中然后通过eval或者构造Function的方式来“安装”这个库。更优雅和可维护的方式是利用Postman的“全局变量”或“环境变量”将库的源码存储为一个长长的字符串变量然后在脚本开头通过eval(pm.globals.get(smCryptoLibCode))这样的方式来引入。这确保了库代码在所有请求中可复用且易于更新。2.3 算法模式与填充理解你的加密参数在开始写代码之前必须和开发确认清楚加密的所有参数任何一项不匹配都会导致加解密失败。以下是关键参数解析算法AES、SM4。这是根本。密钥Key加密和解密的钥匙。需要确认长度如AES-128/192/256对应16/24/32字节和格式通常是Hex十六进制字符串或Base64字符串。模式ModeECB电子密码本模式。最简单同一密钥下相同明文块加密结果相同安全性较弱不推荐用于敏感数据。CBC密码分组链接模式。最常用的模式之一需要初始化向量IV。IV的作用是使相同的明文每次加密产生不同的密文提升安全性。IV通常需要和密钥一起提供给解密方。GCM伽罗瓦/计数器模式。这是一种认证加密模式不仅能保密还能验证数据完整性防篡改。它会产出密文和一个认证标签Auth Tag。填充Padding因为分组密码算法如AES、SM4按固定块大小如128位处理数据明文长度不是块大小的整数倍时就需要填充。PKCS5/PKCS7最常用的填充方式。本质上在PKCS5的上下文中对于AES这类16字节块大小的算法PKCS5和PKCS7是等价的。NoPadding无填充。要求明文长度必须是块大小的整数倍否则会出错。输出格式加密后的结果通常是一个二进制数据CipherParams对象我们需要将其转换为字符串以便在HTTP请求中传输。最常见的是Base64和Hex十六进制。必须确认后端期望哪种格式。对于SM3它是哈希算法散列函数用于生成消息摘要或签名不可逆。通常用于计算参数的签名Sign而非对请求体整体加密。3. 实战配置在Postman中集成SM-Crypto库由于AES有内置支持我们重点解决国密算法的引入问题。我们将sm-crypto库集成到Postman中。第一步获取sm-crypto库源码访问sm-crypto的GitHub仓库例如https://github.com/JuneAndGreen/sm-crypto或通过npm获取其浏览器构建版本如sm-crypto.min.js。我们需要的是那个独立的、包含所有功能的单个JS文件内容。第二步将库源码存入Postman全局变量在Postman中点击右上角的眼睛图标进入环境/全局变量管理界面。切换到“Globals”标签页。点击“Add”新建一个变量。变量名可以设为smCryptoLib。将sm-crypto库的完整、单文件的JS源码全部复制粘贴到“Initial value”和“Current value”中。这是一个非常长的字符串。点击“Save”保存。注意全局变量对所有工作区请求可见。如果你只在特定项目中使用也可以将其存入“环境变量”中。关键在于这个源码字符串要能被脚本访问到。第三步编写通用的库加载脚本为了避免在每个请求的Pre-request和Tests脚本中都重复写加载代码我们可以创建一个Postman的全局脚本虽然Postman没有传统意义上的全局脚本但我们可以通过一个变通方法将加载逻辑写成一个函数存入另一个全局变量。更简单的做法是将加载逻辑封装成一个可复用的代码片段。下面是一个安全的加载函数示例你可以将其保存在一个文本编辑器中随时复制使用// 函数安全地加载并返回smCrypto对象 const loadSmCrypto () { // 检查是否已加载避免重复执行eval if (typeof smCrypto ! undefined) { return smCrypto; } // 从全局变量中获取库源码 const libCode pm.globals.get(smCryptoLib); if (!libCode) { throw new Error(smCrypto库源码未在全局变量中找到请检查变量名是否为“smCryptoLib”。); } // 在一个新的函数作用域中执行库代码避免污染全局环境 const loadScript new Function(libCode \nreturn { sm2, sm3, sm4 };); const cryptoLib loadScript(); // 将返回的对象赋值给一个全局可访问的变量在Postman脚本上下文中 globalThis.smCrypto cryptoLib; return cryptoLib; }; // 调用函数获取smCrypto对象 let smCrypto; try { smCrypto loadSmCrypto(); console.log(sm-crypto库加载成功); } catch (error) { console.error(加载sm-crypto库失败:, error.message); // 可以根据需要设置一个标记让后续逻辑不再执行加密操作 }将上述代码块放在你的Pre-request Script或Tests Script的最前面。现在你就可以通过smCrypto.sm2smCrypto.sm3smCrypto.sm4来调用国密算法了。4. 编写加解密函数针对AES与SM4的完整示例有了库的支持我们就可以编写具体的加解密函数了。这里我们分别给出AES使用内置CryptoJS和SM4使用引入的sm-crypto的示例。4.1 AES加解密函数使用内置CryptoJS假设场景AES-128-CBC模式PKCS7填充密钥和IV为16字节Hex字符串输出为Base64。// AES 加解密函数 (使用内置CryptoJS) /** * AES加密 (CBC模式PKCS7填充) * param {string} plainText - 待加密的明文 * param {string} keyHex - 16进制格式的密钥16/24/32字节对应128/192/256位 * param {string} ivHex - 16进制格式的初始化向量16字节 * param {string} outputFormat - 输出格式base64 或 hex默认base64 * returns {string} 加密后的密文字符串 */ function aesEncrypt(plainText, keyHex, ivHex, outputFormat base64) { try { // CryptoJS期望的Key和IV是WordArray对象可以从Hex字符串转换 const key CryptoJS.enc.Hex.parse(keyHex); const iv CryptoJS.enc.Hex.parse(ivHex); // 执行加密 const encrypted CryptoJS.AES.encrypt(plainText, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 // 对于AESPkcs7即等同于Pkcs5 }); // 根据要求格式输出 if (outputFormat.toLowerCase() hex) { return encrypted.ciphertext.toString(CryptoJS.enc.Hex); } else { // 默认返回Base64 return encrypted.toString(); } } catch (error) { console.error(AES加密失败:, error); throw new Error(AES加密失败: ${error.message}); } } /** * AES解密 (CBC模式PKCS7填充) * param {string} cipherText - 待解密的密文Base64或Hex字符串 * param {string} keyHex - 16进制格式的密钥 * param {string} ivHex - 16进制格式的初始化向量 * param {string} inputFormat - 输入密文格式base64 或 hex默认base64 * returns {string} 解密后的明文字符串 */ function aesDecrypt(cipherText, keyHex, ivHex, inputFormat base64) { try { const key CryptoJS.enc.Hex.parse(keyHex); const iv CryptoJS.enc.Hex.parse(ivHex); // 根据输入格式将密文字符串转换为CipherParams对象 let cipherParams; if (inputFormat.toLowerCase() hex) { // 如果是Hex格式需要先还原为WordArray const ciphertextHex CryptoJS.enc.Hex.parse(cipherText); // 创建一个CipherParams对象CryptoJS内部解密时需要 cipherParams CryptoJS.lib.CipherParams.create({ ciphertext: ciphertextHex }); } else { // 对于Base64CryptoJS.enc.Base64.parse可能不直接适用使用CryptoJS.AES.decrypt本身能处理Base64字符串 // 这里直接传递字符串CryptoJS会识别 cipherParams cipherText; } const decrypted CryptoJS.AES.decrypt(cipherParams, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); // 将解密结果WordArray转换为UTF-8字符串 return decrypted.toString(CryptoJS.enc.Utf8); } catch (error) { console.error(AES解密失败:, error); throw new Error(AES解密失败: ${error.message}); } } // 使用示例 // 假设你的密钥和IV存储在环境变量中 const aesKey pm.environment.get(AES_KEY) || 0123456789abcdef0123456789abcdef; // 32位hex128位密钥 const aesIv pm.environment.get(AES_IV) || 0123456789abcdef0123456789abcdef; // 32位hex // 加密示例 const originalBody { userId: 10001, action: query }; const plainText JSON.stringify(originalBody); const encryptedBodyBase64 aesEncrypt(plainText, aesKey, aesIv, base64); console.log(加密后的Body (Base64):, encryptedBodyBase64); // 将加密结果设置到请求Body中例如raw JSON格式但内容是一个加密字符串 pm.request.body.update({ mode: raw, raw: JSON.stringify({ data: encryptedBodyBase64 }) // 根据后端接口要求可能直接传密文也可能包装在一个字段里 }); // 解密示例通常在Tests脚本中 // const responseBody pm.response.text(); // const decryptedText aesDecrypt(responseBody, aesKey, aesIv, base64); // console.log(解密后的响应:, decryptedText); // const jsonData JSON.parse(decryptedText); // pm.environment.set(decrypted_response, JSON.stringify(jsonData));4.2 SM4加解密函数使用sm-crypto假设场景SM4-CBC模式PKCS7填充密钥和IV为16字节Hex字符串输出为Hex。// 首先确保加载了sm-crypto库 // 使用第3部分定义的loadSmCrypto函数 let smCrypto; try { // 假设loadSmCrypto函数已定义或直接使用前面的代码 // 这里简化为直接调用之前定义的函数 smCrypto loadSmCrypto(); // 这个函数需要提前定义或包含 if (!smCrypto) { throw new Error(smCrypto对象未加载成功); } } catch (error) { console.error(初始化sm-crypto失败:, error); // 可以设置一个标记阻止后续加密操作 } // SM4 加解密函数 /** * SM4加密 (CBC模式PKCS7填充) * param {string} plainText - 待加密的明文 * param {string} keyHex - 16进制格式的密钥32位hex字符16字节 * param {string} ivHex - 16进制格式的初始化向量32位hex字符16字节ECB模式可传空 * param {string} mode - 模式cbc 或 ecb默认cbc * param {string} outputEncoding - 输出编码hex 或 base64默认hex * returns {string} 加密后的密文字符串 */ function sm4Encrypt(plainText, keyHex, ivHex , mode cbc, outputEncoding hex) { if (!smCrypto || !smCrypto.sm4) { throw new Error(sm4加密功能不可用请检查sm-crypto库是否加载成功。); } try { let encrypted; if (mode.toLowerCase() ecb) { // ECB模式不需要IV encrypted smCrypto.sm4.encrypt(plainText, keyHex, { mode: ecb, outputEncoding: outputEncoding }); } else { // CBC模式需要IV if (!ivHex || ivHex.length ! 32) { // 16字节 32位hex throw new Error(CBC模式需要提供32位十六进制字符的IV。); } encrypted smCrypto.sm4.encrypt(plainText, keyHex, { mode: cbc, iv: ivHex, outputEncoding: outputEncoding }); } return encrypted; } catch (error) { console.error(SM4加密失败:, error); throw new Error(SM4加密失败: ${error.message}); } } /** * SM4解密 * param {string} cipherText - 待解密的密文Hex或Base64字符串 * param {string} keyHex - 16进制格式的密钥 * param {string} ivHex - 16进制格式的初始化向量ECB模式可传空 * param {string} mode - 模式cbc 或 ecb默认cbc * param {string} inputEncoding - 输入密文编码hex 或 base64默认hex * returns {string} 解密后的明文字符串 */ function sm4Decrypt(cipherText, keyHex, ivHex , mode cbc, inputEncoding hex) { if (!smCrypto || !smCrypto.sm4) { throw new Error(sm4解密功能不可用请检查sm-crypto库是否加载成功。); } try { let decrypted; if (mode.toLowerCase() ecb) { decrypted smCrypto.sm4.decrypt(cipherText, keyHex, { mode: ecb, inputEncoding: inputEncoding }); } else { if (!ivHex || ivHex.length ! 32) { throw new Error(CBC模式需要提供32位十六进制字符的IV。); } decrypted smCrypto.sm4.decrypt(cipherText, keyHex, { mode: cbc, iv: ivHex, inputEncoding: inputEncoding }); } return decrypted; } catch (error) { console.error(SM4解密失败:, error); throw new Error(SM4解密失败: ${error.message}); } } // SM3 哈希计算函数 function sm3Hash(data) { if (!smCrypto || !smCrypto.sm3) { throw new Error(sm3哈希功能不可用请检查sm-crypto库是否加载成功。); } return smCrypto.sm3(data); // sm3函数通常直接返回16进制哈希字符串 } // 使用示例 // 假设密钥和IV存储在环境变量中 const sm4Key pm.environment.get(SM4_KEY) || 0123456789abcdef0123456789abcdef; // 32位hex const sm4Iv pm.environment.get(SM4_IV) || 0123456789abcdef0123456789abcdef; // 32位hex // 1. 加密请求体 const requestData { certNo: 110101199001011234, mobile: 13800138000 }; const plainTextForSm4 JSON.stringify(requestData); const encryptedDataHex sm4Encrypt(plainTextForSm4, sm4Key, sm4Iv, cbc, hex); console.log(SM4加密后的数据 (Hex):, encryptedDataHex); // 更新请求体例如以表单数据或JSON格式发送 pm.request.body.update({ mode: raw, raw: JSON.stringify({ encryptedData: encryptedDataHex }) }); // 2. 计算签名例如将某些参数排序后拼接再进行SM3哈希 const signParams { appId: your_app_id, timestamp: Date.now().toString(), data: encryptedDataHex }; // 按参数名ASCII码从小到大排序拼接成键值对字符串最后加上密钥 const signString Object.keys(signParams).sort().map(key ${key}${signParams[key]}).join() key${sm4Key}; const signature sm3Hash(signString); console.log(生成的SM3签名:, signature); // 将签名放入请求头 pm.request.headers.add({ key: X-Signature, value: signature }); // 3. 解密响应在Tests脚本中 // const encryptedResponse pm.response.text(); // 假设响应体就是Hex格式的密文 // const decryptedResponseText sm4Decrypt(encryptedResponse, sm4Key, sm4Iv, cbc, hex); // console.log(SM4解密后的响应:, decryptedResponseText); // pm.test(响应解密成功, function () { // const jsonResp JSON.parse(decryptedResponseText); // pm.expect(jsonResp.code).to.eql(0); // });5. 构建自动化测试工作流变量、集合与脚本的联动有了基础的加解密函数下一步是将其融入Postman的自动化测试工作流实现真正的“一键测试”。5.1 利用环境变量与全局变量管理密钥和配置永远不要将密钥等敏感信息硬编码在脚本里。Postman的变量系统是管理这些配置的最佳场所。环境变量Environment Variables为不同的测试环境开发、测试、预生产设置不同的密钥、IV和接口地址。例如创建DEV环境变量SM4_KEY,SM4_IV,BASE_URL。切换到TEST环境时这些值会自动更换。全局变量Global Variables存放一些跨环境共享的配置比如smCryptoLib库源码或者通用的加密函数配置如默认的outputEncoding。集合变量Collection Variables如果一套接口都使用相同的加解密方式可以将密钥和IV定义在集合级别该集合下的所有请求都可以继承使用。在脚本中使用pm.environment.get(KEY_NAME)和pm.collectionVariables.get(KEY_NAME)来获取这些值。5.2 在集合级别定义预请求脚本和测试脚本如果你有多个接口都需要相同的加解密逻辑在每个请求里重复写脚本是低效的。Postman允许在集合Collection级别定义Pre-request Script和Tests Script。这些脚本会在集合内每个请求的对应阶段执行。你可以把加载加密库的通用代码、以及获取基础密钥变量的逻辑放在集合的Pre-request Script中。这样集合内的每个请求脚本一运行就已经有了可用的加密库和密钥。集合级Pre-request Script示例// 集合级Pre-request Script: 初始化加密环境 console.log(运行在集合【${pm.collection.name}】的预请求脚本); // 1. 加载sm-crypto库如果用到 try { if (!globalThis.smCrypto) { const libCode pm.globals.get(smCryptoLib); if (libCode) { const loadScript new Function(libCode \nreturn { sm2, sm3, sm4 };); globalThis.smCrypto loadScript(); console.log(集合级脚本sm-crypto库加载成功。); } } } catch (error) { console.error(集合级脚本加载sm-crypto库失败, error); } // 2. 定义全局可用的加密函数可选也可以在每个请求脚本中定义 // 这里可以定义 aesEncrypt, sm4Encrypt 等函数并挂载到 globalThis 上 // 例如globalThis.myCrypto { aesEncrypt, sm4Encrypt };5.3 参数化与数据驱动测试这是自动化测试的精华。你可以将测试数据明文放在一个CSV或JSON文件中通过Postman的Collection Runner或Newman命令行工具来运行。准备数据文件创建一个CSV文件例如test_data.csv包含列testCaseId,plainJson,expectedCode。testCaseId,plainJson,expectedCode TC01,{name:张三,idCard:110101199001011234},0 TC02,{name:李四,idCard:},1001 # 身份证为空期望返回错误码1001在请求脚本中引用数据在Pre-request Script中使用pm.iterationData.get(plainJson)来获取当前迭代的测试数据。// 在请求的Pre-request Script中 const plainJsonString pm.iterationData.get(plainJson); const key pm.environment.get(SM4_KEY); const iv pm.environment.get(SM4_IV); // 加密数据 const encryptedData sm4Encrypt(plainJsonString, key, iv, cbc, hex); // 更新请求体 pm.request.body.update({ mode: raw, raw: JSON.stringify({ data: encryptedData }) }); // 将期望的结果也存入环境变量供Tests脚本断言使用 pm.environment.set(expectedCode, pm.iterationData.get(expectedCode));在Tests脚本中断言解密响应后使用pm.expect对解密后的业务数据进行断言并与pm.environment.get(expectedCode)进行比对。// 解密响应 const encryptedResponse pm.response.text(); const decryptedText sm4Decrypt(encryptedResponse, key, iv, cbc, hex); const actualResponse JSON.parse(decryptedText); // 断言 pm.test(业务状态码正确, function () { pm.expect(actualResponse.code).to.eql(parseInt(pm.environment.get(expectedCode))); }); pm.test(响应数据解密成功, function () { pm.expect(actualResponse).to.have.property(data); });通过这种方式你只需要准备好测试数据文件然后运行集合Postman就会自动用每一行数据去加密、发送请求、解密响应并验证结果实现完全的自动化。6. 高级技巧与疑难问题排查在实际使用中你可能会遇到一些棘手的问题。这里分享一些经验和排查思路。6.1 处理GCM模式等高级加密模式AES-GCM模式在CryptoJS中可能需要特别注意。GCM模式会产生一个认证标签Authentication Tag这个标签需要和密文一起传输给接收方用于验证。CryptoJS的encrypt方法在GCM模式下返回的对象结构略有不同。function aesGcmEncrypt(plainText, keyHex, ivHex, aad ) { const key CryptoJS.enc.Hex.parse(keyHex); const iv CryptoJS.enc.Hex.parse(ivHex); const additionalData CryptoJS.enc.Utf8.parse(aad); // 附加认证数据可选 const encrypted CryptoJS.AES.encrypt(plainText, key, { iv: iv, mode: CryptoJS.mode.GCM, padding: CryptoJS.pad.NoPadding, // GCM通常使用NoPadding additionalData: additionalData // 设置AAD }); // 密文 const ciphertextBase64 encrypted.ciphertext.toString(CryptoJS.enc.Base64); // 认证标签非常重要 const authTagBase64 encrypted.tag.toString(CryptoJS.enc.Base64); return { ciphertext: ciphertextBase64, tag: authTagBase64 }; } // 发送时需要将ciphertext和tag都传给后端通常放在JSON的不同字段里。解密时需要同时提供密文和tag。function aesGcmDecrypt(ciphertextBase64, keyHex, ivHex, authTagBase64, aad ) { const key CryptoJS.enc.Hex.parse(keyHex); const iv CryptoJS.enc.Hex.parse(ivHex); const tag CryptoJS.enc.Base64.parse(authTagBase64); const additionalData CryptoJS.enc.Utf8.parse(aad); // 重组CipherParams对象必须包含ciphertext和tag const cipherParams CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Base64.parse(ciphertextBase64), tag: tag // 关键 }); const decrypted CryptoJS.AES.decrypt(cipherParams, key, { iv: iv, mode: CryptoJS.mode.GCM, padding: CryptoJS.pad.NoPadding, additionalData: additionalData }); return decrypted.toString(CryptoJS.enc.Utf8); }6.2 编码与格式的坑Hex、Base64与字符串转换这是加解密失败最常见的原因之一。密钥/IV格式确认开发给的密钥是Hex字符串还是Base64字符串。一个16字节的密钥Hex表示是32个字符0-9, a-fBase64表示是24个字符末尾可能有。在代码中要用对应的解析方法CryptoJS.enc.Hex.parse或CryptoJS.enc.Base64.parse。密文格式加密后输出的是什么是Hex字符串还是Base64字符串解密时输入的格式必须匹配。sm-crypto的outputEncoding/inputEncoding参数就是用来控制这个的。字符串编码明文在加密前通常需要是字符串。如果是JSON对象要先JSON.stringify()。解密后得到的WordArray或字符串也要用正确的编码如CryptoJS.enc.Utf8转回来。一个实用的调试方法用一个已知的、能正常工作的加解密工具如OpenSSL命令行、一个可靠的在线工具和你的Postman脚本用相同的密钥、IV、明文进行加密对比输出的密文是否完全一致。如果不一致逐个参数检查模式、填充、输出格式。6.3 性能考量与脚本优化当测试数据量很大时在Pre-request Script中进行复杂的加密计算可能会略微影响请求发送速度。虽然对于单次接口测试影响微乎其微但在数据驱动测试的成百上千次迭代中累积起来可能可观。缓存库对象确保smCrypto或加密函数只被初始化一次而不是每次请求都重新eval库源码。这就是为什么我们在集合脚本或通过globalThis来缓存它。简化逻辑检查你的脚本避免不必要的循环或复杂计算。使用Newman对于大规模的自动化测试建议使用Postman的命令行工具Newman在服务器上运行其性能通常优于图形化界面的Collection Runner。6.4 常见错误与排查清单Error: Malformed UTF-8 data解密后转换UTF-8字符串时出错。几乎可以肯定是解密失败了得到的二进制数据根本不是有效的明文。请检查密钥、IV、模式、填充、密文格式是否全部与后端一致。Error: Invalid key length密钥长度不对。AES-128需要16字节32位HexAES-256需要32字节64位Hex。SM4固定为16字节32位Hex。检查你的密钥变量是否正确获取并解析。smCrypto is not definedsm-crypto库没有加载成功。检查全局变量smCryptoLib是否存在且内容完整检查加载代码的eval或Function构造是否正确在脚本开头加console.log(pm.globals.get(smCryptoLib).substring(0,100))看看是否拿到了库代码的前100个字符。后端返回“解密失败”网络抓包对比用Fiddler或Charles抓取一个从客户端如App发出的成功请求和你Postman发出的请求进行对比。重点关注Body里的密文字符串是否完全一样。日志输出在Postman的ConsoleView - Show Postman Console中详细打印出加密前的明文、使用的密钥、IV、以及加密后的结果。将这些信息提供给开发让他们用相同的参数在服务端解密看是否能成功。检查请求格式密文是放在JSON的某个字段里还是直接作为Raw文本Content-Type头是否正确如application/json无法解密响应首先确认响应体确实是加密的可能是一串规律的Hex或Base64码。有些接口错误时可能返回明文错误信息。先console.log(pm.response.text())看看原始响应是什么。如果是加密的再套用解密函数。将加解密逻辑整合进Postman虽然前期需要一些配置和脚本编写工作但它所带来的测试效率提升和流程标准化收益是巨大的。它使得加密接口的测试变得像测试普通明文接口一样简单直观特别适合在敏捷开发和持续集成流程中落地。当你熟悉了这套模式后无论是面对AES、SM4还是其他加密算法你都能快速构建出对应的自动化测试方案从容应对各种安全接口的测试挑战。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进