
1. 项目概述为什么要在Postman里折腾加解密做接口测试和联调的朋友估计没少在Postman里跟各种加密接口“斗智斗勇”。你兴冲冲地拿到一个接口文档一看请求参数好家伙整个body被一个叫encryptedData的字段包着里面是一串看不懂的密文。或者响应回来明明状态码是200但返回体是一堆乱码文档上轻描淡写地写着“需用SM4解密后查看”。这时候如果你还停留在手动复制密文、打开某个在线加解密网站、粘贴、选择算法、再复制结果回Postman的原始阶段那效率就太低了而且极易出错。这个项目的核心就是把加解密这个“脏活累活”集成到Postman的工作流内部。我们不是要研究AES、SM3、SM4这些算法本身有多深奥那是密码学家的事而是要解决一个非常实际的工程问题如何让Postman在发送请求前自动加密明文在收到响应后自动解密密文让测试人员像处理普通JSON接口一样透明地处理加解密接口。AES是国际通用算法SM3和SM4则是国密算法在国内金融、政务等领域应用非常广泛。处理这类接口通常有几种“野路子”一是写个外部脚本用Python或Node.js处理好再手动粘贴结果二是在Postman的Pre-request Script或Tests里写一堆零散的代码。前者割裂了工作流后者往往代码混乱、难以复用。我们的目标是建立一个清晰、可复用、易维护的Postman加解密解决方案。它应该像使用环境变量一样方便通过预置的脚本函数和清晰的配置让加解密对测试过程“隐身”。接下来我会拆解整个设计思路和实操细节这套方法经过多个金融项目实战检验你可以直接拿去用。2. 整体方案设计与核心思路拆解在Postman里实现自动化加解密本质是利用其强大的脚本执行能力。我们需要在两个关键节点注入逻辑请求发送前Pre-request Script将准备好的明文数据如JSON对象按照接口要求加密并赋值给请求体或特定参数。收到响应后Tests Script拦截响应体如果是密文则进行解密并将解密后的明文保存到一个变量中方便后续断言或查看。2.1 方案选型集中化管理 vs. 分散式脚本这里有两个主流思路方案A每个接口独立编写脚本这是新手最容易掉进去的坑。在每个需要加解密的接口的Pre-request和Tests标签页里直接写入完整的加解密代码。缺点极其明显代码重复率高一旦密钥或算法模式变更需要修改所有相关接口维护是灾难。方案B函数库环境变量集中管理推荐这是我们将要采用的工业级做法。核心思想是建立加解密函数库在Postman的“集合”Collection级别编写通用的加解密函数如encryptAES,decryptSM4。通过环境变量配置参数将密钥Key、初始向量IV、算法模式等配置信息存放在环境变量中实现与代码的分离。接口脚本轻量化具体接口的脚本里只需一两行代码调用通用函数并传入当前接口所需的参数。方案B的优势在于“一变应万变”。修改算法或密钥只需更新集合脚本或环境变量。新增接口复制粘贴几行调用代码即可。这是工程化的基本思维。2.2 技术栈考量CryptoJS与ForgePostman的脚本环境基于Node.js但并非完整的Node环境很多原生crypto模块的函数不可用。因此我们需要引入第三方库。最主流的选择有两个CryptoJS一个经典的JavaScript加密算法库。优点是非常流行资料多支持AES等算法。但致命缺点是它不支持国密算法SM3和SM4。如果你的项目只涉及AESCryptoJS是一个不错的选择。Forge一个更全面的JavaScript加密工具包。其最大优势是通过社区贡献的插件可以支持SM2、SM3、SM4等国密算法。对于国内项目这几乎是唯一选择。注意由于Postman沙盒环境的限制我们不能直接用npm install。通常需要将库的完整源码通常是一个单独的.js文件如forge.min.js以字符串形式粘贴到集合的“Pre-request Script”顶部或者通过Postman的“全局变量”来引入。本文将基于Forge库进行讲解因为它能覆盖AES和国密的全场景。2.3 核心流程设计整个自动化流程可以抽象为以下几步我画个简单的顺序图帮你理解测试人员输入明文参数 - Pre-request Script 触发 - 调用加密函数(使用环境变量中的密钥) - 生成密文并替换请求体 - Postman发送加密请求 - 服务器处理并返回加密响应 - Tests Script 触发 - 调用解密函数 - 得到明文并存入变量 - 测试人员查看明文结果或进行断言这个流程的关键在于测试人员感知到的始终是“明文”加解密过程被封装在后台。3. 环境准备与Forge库引入工欲善其事必先利其器。第一步就是搭建好我们的“武器库”。3.1 获取Forge库及其国密扩展获取基础Forge库访问Forge的GitHub仓库或通过CDN获取forge.min.js文件。你可以搜索“forge js cdn”找到最新版本的链接。获取国密扩展Forge本身不支持国密需要额外的扩展。你需要寻找forge-sm3.js和forge-sm4.js这样的扩展文件。这些通常由国内开发者贡献可以在GitHub或一些技术博客上找到。务必注意扩展文件的兼容性和安全性最好从相对知名的开源项目获取。3.2 在Postman中创建函数库我们不推荐将庞大的库代码直接写在每个集合的脚本里。最佳实践是利用全局变量或集合变量来存储库代码。步骤一创建初始化请求新建一个“辅助性”的请求这个方法很巧妙。你可以将其命名为“[Init] Load Crypto Lib”URL随便填比如https://postman.com并且永远不需要真正发送它。在它的Tests标签页里粘贴以下代码// 将forge库源码以字符串形式赋值给一个全局变量 pm.globals.set(forge_lib, /* 这里粘贴完整的 forge.min.js 文件内容 */); // 同样设置国密扩展 pm.globals.set(forge_sm3_lib, /* 粘贴 forge-sm3.js 内容 */); pm.globals.set(forge_sm4_lib, /* 粘贴 forge-sm4.js 内容 */); console.log(“加密库已加载到全局变量。”);运行一次这个请求点击Send这些库代码就会被保存到Postman的全局变量中供所有集合使用。步骤二在集合的Pre-request Script中引入库打开你的项目集合进入Pre-request Script标签页。在这里我们需要将全局变量里的库代码“激活”成可用的脚本。// 从全局变量中获取库代码字符串 const forgeLibCode pm.globals.get(“forge_lib”); const sm3LibCode pm.globals.get(“forge_sm3_lib”); const sm4LibCode pm.globals.get(“forge_sm4_lib”); // 使用eval执行库代码注意在受信任的代码中可谨慎使用 eval(forgeLibCode); eval(sm3LibCode); eval(sm4LibCode); // 此时forge 和 sm4 等对象应该已经在作用域内了 // 可以写个简单的测试验证 try { console.log(“Forge版本”, forge.version); console.log(“SM4支持情况”, typeof sm4 ! ‘undefined’ ? ‘已加载’ : ‘未加载’); } catch (e) { console.error(“库加载失败”, e.message); }重要提示eval函数存在安全风险但这里我们eval的是自己从可信源获取并存入全局变量的代码在Postman封闭的测试环境中是可控的、可接受的方案。这是目前Postman内使用自定义JS库最实用的方法。3.3 配置环境变量接下来我们将所有可变的配置参数放入环境变量。创建一个新的环境例如命名为“Crypto_Config”并添加以下变量变量名示例值说明aes_key0123456789abcdef0123456789abcdefAES密钥32位十六进制字符串对应AES-256aes_ivabcdef0123456789AES CBC模式的初始向量16位十六进制字符串sm4_key0123456789abcdef0123456789abcdefSM4密钥32位十六进制字符串sm4_ivabcdef0123456789SM4 CBC模式的初始向量16位十六进制字符串crypto_modeCBC加密模式如 CBC, ECB, GCM 等data_encodingbase64密文的编码方式hex或base64这样当切换测试环境如从测试环境切到生产环境时只需切换不同的环境密钥等配置会自动切换无需修改任何脚本。4. 核心加解密函数编写与解析有了库和环境变量我们就可以编写核心工具函数了。我们将这些函数写在集合的Pre-request Script里这样集合下的所有接口都能调用。4.1 AES加解密函数实现我们以实现最常用的AES-256-CBC模式为例并考虑PKCS7填充Postman中常对应PKCS5Padding两者在AES语境下通常等价。// AES 加解密函数 /** * AES加密 (CBC模式 PKCS7填充) * param {string} plainText - 待加密的明文 * param {string} key - 十六进制格式的密钥 * param {string} iv - 十六进制格式的初始向量 * param {string} outputEncoding - 输出编码‘base64’ 或 ‘hex’ * return {string} 加密后的密文 */ function encryptAES(plainText, key, iv, outputEncoding ‘base64’) { // 将十六进制字符串的密钥和IV转换为Forge需要的字节缓冲区 const keyBytes forge.util.hexToBytes(key); const ivBytes forge.util.hexToBytes(iv); // 创建Cipher对象 const cipher forge.cipher.createCipher(‘AES-CBC’, forge.util.createBuffer(keyBytes)); cipher.start({ iv: forge.util.createBuffer(ivBytes) }); // 添加要加密的数据 cipher.update(forge.util.createBuffer(plainText, ‘utf8’)); cipher.finish(); // 获取加密结果并按指定格式输出 const encrypted cipher.output; if (outputEncoding ‘base64’) { return forge.util.encode64(encrypted.getBytes()); } else { return encrypted.toHex(); } } /** * AES解密 * param {string} cipherText - 密文 * param {string} key - 十六进制格式的密钥 * param {string} iv - 十六进制格式的初始向量 * param {string} inputEncoding - 密文编码‘base64’ 或 ‘hex’ * return {string} 解密后的明文 */ function decryptAES(cipherText, key, iv, inputEncoding ‘base64’) { const keyBytes forge.util.hexToBytes(key); const ivBytes forge.util.hexToBytes(iv); // 将密文转换为字节缓冲区 let encryptedBytes; if (inputEncoding ‘base64’) { encryptedBytes forge.util.decode64(cipherText); } else { encryptedBytes forge.util.hexToBytes(cipherText); } const decipher forge.cipher.createDecipher(‘AES-CBC’, forge.util.createBuffer(keyBytes)); decipher.start({ iv: forge.util.createBuffer(ivBytes) }); decipher.update(forge.util.createBuffer(encryptedBytes)); const result decipher.finish(); if (result) { return decipher.output.toString(‘utf8’); } else { throw new Error(‘AES解密失败可能是密钥或IV错误。’); } }关键点解析编码转换这是最容易出错的地方。接口文档给的密钥/IV通常是十六进制字符串而Forge内部操作的是字节。forge.util.hexToBytes()和forge.util.createBuffer()完成了这个关键转换。填充模式Forge的AES-CBC默认使用PKCS7填充这与国内文档常写的PKCS5Padding兼容。错误处理解密函数中decipher.finish()返回一个布尔值表示解密是否成功。失败通常意味着密钥、IV或密文格式错误。4.2 SM4加解密函数实现SM4是国密对称算法其函数设计与AES非常相似主要区别在于算法标识。// SM4 加解密函数 (假设已通过扩展库引入sm4对象) /** * SM4加密 (CBC模式) * param {string} plainText - 明文 * param {string} key - 十六进制密钥 (32位) * param {string} iv - 十六进制初始向量 (32位 SM4的IV也是16字节但常以32位十六进制字符串表示) * param {string} outputEncoding - ‘base64’ 或 ‘hex’ * return {string} 密文 */ function encryptSM4(plainText, key, iv, outputEncoding ‘base64’) { // 确保国密扩展已加载 if (typeof sm4 ‘undefined’) { throw new Error(‘SM4扩展库未加载请检查初始化脚本。’); } const keyBytes forge.util.hexToBytes(key); const ivBytes forge.util.hexToBytes(iv); // 注意不同扩展库的API可能略有不同此处为示例 // 假设扩展库提供了 sm4.encryptCBC 函数 const cipher sm4.encryptCBC( forge.util.createBuffer(keyBytes), forge.util.createBuffer(ivBytes), forge.util.createBuffer(plainText, ‘utf8’) ); if (outputEncoding ‘base64’) { return forge.util.encode64(cipher.getBytes()); } else { return cipher.toHex(); } } /** * SM4解密 * param {string} cipherText - 密文 * param {string} key - 十六进制密钥 * param {string} iv - 十六进制初始向量 * param {string} inputEncoding - ‘base64’ 或 ‘hex’ * return {string} 明文 */ function decryptSM4(cipherText, key, iv, inputEncoding ‘base64’) { if (typeof sm4 ‘undefined’) { throw new Error(‘SM4扩展库未加载。’); } const keyBytes forge.util.hexToBytes(key); const ivBytes forge.util.hexToBytes(iv); let encryptedBytes; if (inputEncoding ‘base64’) { encryptedBytes forge.util.decode64(cipherText); } else { encryptedBytes forge.util.hexToBytes(cipherText); } const plainBuffer sm4.decryptCBC( forge.util.createBuffer(keyBytes), forge.util.createBuffer(ivBytes), forge.util.createBuffer(encryptedBytes) ); return plainBuffer.toString(‘utf8’); }实操心得国密扩展库的API五花八门没有统一标准。上面代码中的sm4.encryptCBC只是一个示例。你实际使用时一定要仔细阅读你所引入的那个扩展库的文档或源码查看其具体的函数名和参数顺序。这是集成国密算法时最大的一个“坑”。4.3 SM3摘要函数实现SM3是国密哈希算法类似于SHA-256用于生成消息摘要或签名。它不需要密钥。// SM3 摘要函数 /** * 生成SM3摘要 * param {string} message - 原始消息 * param {string} outputEncoding - 输出编码‘hex’ 或 ‘base64’ * return {string} 摘要值 */ function generateSM3(message, outputEncoding ‘hex’) { if (typeof sm3 ‘undefined’) { throw new Error(‘SM3扩展库未加载。’); } // 假设扩展库提供了 sm3.hash 函数 const md sm3.hash(forge.util.createBuffer(message, ‘utf8’)); if (outputEncoding ‘base64’) { return forge.util.encode64(md.getBytes()); } else { return md.toHex(); } }4.4 将函数设为全局可用仅仅在集合脚本里定义函数还不够我们需要让集合下的每个请求都能调用它们。在Postman中可以通过将函数赋值给全局对象pm来实现。在集合的Pre-request Script最底部添加// 将工具函数挂载到pm对象上方便在请求脚本中调用 pm.globals.set(‘encryptAES’, encryptAES.toString()); pm.globals.set(‘decryptAES’, decryptAES.toString()); pm.globals.set(‘encryptSM4’, encryptSM4.toString()); pm.globals.set(‘decryptSM4’, decryptSM4.toString()); pm.globals.set(‘generateSM3’, generateSM3.toString());但注意这样存储的是函数字符串。更好的方式是利用JavaScript的作用域因为集合的Pre-request Script会在每个集合内的请求之前执行所以直接在那里定义的函数在其后的请求脚本中本就是可用的。更保险的做法是将上述函数定义代码完整地放在集合的Pre-request Script中它们就会成为该集合下所有请求的共享上下文。5. 在接口中实战应用请求加密与响应解密现在我们有了“武器库”可以开始实战了。假设我们有一个用户查询接口/api/user/query要求请求体用SM4加密响应体也是SM4加密的。5.1 配置请求与参数在集合下新建一个请求方法设为POSTURL填上你的接口地址。在Headers标签页设置Content-Type: application/json即使body是密文通常也这么设置。在Body标签页先选择raw和JSON并输入一个明文的JSON结构。这是我们作为测试人员希望发送的数据。{ “userId”: “100001”, “timestamp”: “2023-10-27 14:30:00” }5.2 编写Pre-request Script请求加密切换到该请求的Pre-request Script标签页。这里的逻辑是在请求被发送出去之前拦截我们填好的明文Body加密它然后用密文替换Body。// 1. 从环境变量获取配置 const sm4Key pm.environment.get(“sm4_key”); // 例如: “0123456789abcdef0123456789abcdef” const sm4Iv pm.environment.get(“sm4_iv”); // 例如: “abcdef0123456789abcdef0123456789” const encoding pm.environment.get(“data_encoding”) || “base64”; // 2. 获取当前请求的原始Body明文JSON字符串 const rawBody pm.request.body.raw; console.log(“原始明文请求体”, rawBody); // 3. 调用SM4加密函数该函数已在集合脚本中定义可直接调用 try { const encryptedData encryptSM4(rawBody, sm4Key, sm4Iv, encoding); console.log(“加密后的密文”, encryptedData); // 4. 将请求体更新为加密后的密文 // 通常接口要求将密文放在一个特定的字段里比如 {“encryptedData”: “xxx”} const newBody JSON.stringify({ encryptedData: encryptedData // 有些接口可能还有其他非加密字段也可以在这里一起拼接 }); pm.request.body.update({ mode: ‘raw’, raw: newBody }); // 可选将密文也存到环境变量方便调试查看 pm.environment.set(“last_encrypted_request”, encryptedData); } catch (error) { console.error(“请求加密失败”, error.message); // 可以选择让请求失败 // pm.request.setEnabled(false); }关键点解析pm.request.body.raw可以获取到我们在Body标签页里填写的原始内容。加密后我们通过pm.request.body.update()动态修改了即将发送的请求体。这是一个非常强大的功能。加密后的结构{“encryptedData”: “xxx”}需要严格按照接口文档要求来构造。5.3 编写Tests Script响应解密请求发送后我们会在Tests标签页里处理响应。这里的逻辑是解密响应体并将解密后的明文保存起来。// 1. 检查请求是否成功 if (pm.response.code 200) { // 2. 获取响应体文本通常是密文 const encryptedResponse pm.response.text(); console.log(“接收到的加密响应”, encryptedResponse); // 3. 从环境变量获取解密配置通常与加密配置相同 const sm4Key pm.environment.get(“sm4_key”); const sm4Iv pm.environment.get(“sm4_iv”); const encoding pm.environment.get(“data_encoding”) || “base64”; // 4. 尝试解密 try { // 注意响应体可能直接是密文字符串也可能是一个JSON包裹着密文字段 let cipherText encryptedResponse; // 尝试解析为JSON如果成功则提取密文字段 try { const jsonResp JSON.parse(encryptedResponse); if (jsonResp.encryptedData) { cipherText jsonResp.encryptedData; } else if (jsonResp.data) { // 也可能是其他字段名 cipherText jsonResp.data; } } catch (e) { // 解析失败说明响应体直接就是密文直接使用cipherText console.log(“响应体非JSON格式按原始密文处理。”); } const decryptedText decryptSM4(cipherText, sm4Key, sm4Iv, encoding); console.log(“解密后的明文响应”, decryptedText); // 5. 将解密后的明文保存为环境变量或全局变量方便后续查看或断言 pm.environment.set(“last_decrypted_response”, decryptedText); // 6. 可选将解密后的文本自动解析为JSON并设置到pm.expect的测试对象中 try { const jsonData JSON.parse(decryptedText); pm.expect(jsonData.code).to.eql(0); // 示例断言业务状态码为0 pm.expect(jsonData.data.userId).to.eql(“100001”); } catch (parseError) { console.log(“解密内容非JSON格式”, decryptedText); } } catch (decryptError) { console.error(“响应解密失败”, decryptError.message); // 如果解密失败可以将原始响应设为测试失败 pm.expect.fail(响应解密失败: ${decryptError.message}); } } else { console.log(请求未成功状态码: ${pm.response.code}); }关键点解析pm.response.text()获取的是原始的响应体字符串。解密前需要判断响应体结构。这是另一个常见坑点有的接口直接返回密文字符串有的返回{“data”: “密文”}这样的JSON。代码中通过try-catch尝试解析JSON来兼容这两种情况。解密成功后我们将明文存入环境变量last_decrypted_response。这样即使在Postman的“响应Body”预览窗格里看到的是乱码我们也可以在“环境变量”窗口或通过控制台看到解密后的清晰内容。我们还可以直接对解密后的JSON进行断言实现全自动化的加密接口测试。6. 高级技巧与场景化封装掌握了基础加解密后我们可以玩得更溜一些应对更复杂的场景。6.1 封装通用加解密中间件如果集合内有大量接口需要加解密在每个请求里复制粘贴脚本依然麻烦。我们可以利用Postman的**文件夹Folder**特性。将需要相同加解密规则的接口放到一个文件夹里然后在这个文件夹的Pre-request Script和Tests中编写通用脚本。文件夹级Pre-request Script示例// 假设这个文件夹下的所有接口都使用相同的SM4加密规则 const cryptoConfig { key: pm.environment.get(“sm4_key”), iv: pm.environment.get(“sm4_iv”), encoding: pm.environment.get(“data_encoding”), fieldName: “encryptedData” // 密文在请求体中的字段名 }; // 获取当前请求的原始Body const rawBody pm.request.body.raw; if (rawBody) { try { const encrypted encryptSM4(rawBody, cryptoConfig.key, cryptoConfig.iv, cryptoConfig.encoding); const newBody {}; newBody[cryptoConfig.fieldName] encrypted; pm.request.body.update({ mode: ‘raw’, raw: JSON.stringify(newBody) }); } catch (e) { console.error([Folder Pre-req] 加密失败: ${e.message}); } }文件夹级Tests Script示例if (pm.response.code 200) { const respText pm.response.text(); const cryptoConfig { key: pm.environment.get(“sm4_key”), iv: pm.environment.get(“sm4_iv”), encoding: pm.environment.get(“data_encoding”) }; try { // 尝试提取密文简单逻辑可根据实际情况增强 let cipherText respText; const jsonMatch respText.match(/”encryptedData”:”([^”])”/); if (jsonMatch) { cipherText jsonMatch[1]; } const decrypted decryptSM4(cipherText, cryptoConfig.key, cryptoConfig.iv, cryptoConfig.encoding); pm.environment.set(“last_decrypted_response”, decrypted); console.log(“[Folder Tests] 解密成功:”, decrypted.substring(0, 100) “…”); // 只打印前100字符 } catch (e) { console.log([Folder Tests] 解密跳过或失败: ${e.message} 原始响应: ${respText.substring(0, 200)}); } }这样该文件夹下的每个接口都自动具备了加解密能力无需再单独编写脚本。如果某个接口有特殊要求可以在接口自身的脚本中覆盖文件夹级别的逻辑。6.2 处理多种算法与动态选择有些系统可能根据接口路径或请求头动态决定使用AES还是SM4。我们可以在脚本中根据条件进行判断。// 在集合或文件夹的Pre-request Script中 const algorithm pm.request.headers.get(‘X-Encrypt-Algorithm’) || ‘SM4’; // 从请求头获取算法类型 const rawBody pm.request.body.raw; if (algorithm.toUpperCase() ‘AES’) { const key pm.environment.get(‘aes_key’); const iv pm.environment.get(‘aes_iv’); const encrypted encryptAES(rawBody, key, iv); // … 更新请求体 } else if (algorithm.toUpperCase() ‘SM4’) { const key pm.environment.get(‘sm4_key’); const iv pm.environment.get(‘sm4_iv’); const encrypted encryptSM4(rawBody, key, iv); // … 更新请求体 } else { console.warn(不支持的加密算法: ${algorithm} 将发送明文); }6.3 生成签名SM3 密钥一些接口除了加密还需要对请求参数进行签名以防篡改。常用方案是将所有参数按规则排序拼接后使用SM3或HMAC-SM3生成摘要。/** * 生成带密钥的SM3签名模拟HMAC-SM3 * 注意标准HMAC需要特定算法这里演示一种常见拼接密钥的签名方式 * param {Object} params - 参数字典 * param {string} secretKey - 签名密钥 * return {string} 签名十六进制 */ function generateSignature(params, secretKey) { // 1. 参数排序并拼接成 key1value1key2value2 格式 const sortedKeys Object.keys(params).sort(); const signString sortedKeys.map(key ${key}${params[key]}).join(‘’); // 2. 将密钥拼接到字符串前后具体规则按接口文档来 const stringToSign secretKey signString secretKey; // 3. 计算SM3摘要 return generateSM3(stringToSign, ‘hex’); } // 使用示例 const requestParams { userId: “100001”, amount: “100.00”, timestamp: Date.now().toString() }; const secret “my_sign_secret”; const signature generateSignature(requestParams, secret); console.log(“生成的签名”, signature); // 然后将 signature 作为参数之一与其他参数一起加密或放在请求头中7. 常见问题、调试技巧与避坑指南在实际操作中你肯定会遇到各种问题。下面是我踩过坑后总结出来的经验。7.1 常见错误与排查表问题现象可能原因排查步骤加密失败控制台报错 “Invalid key length”1. 密钥长度不符合算法要求。2. 密钥格式错误非十六进制字符串。1. 检查密钥位数AES-128(16字节/32hex) AES-192(24字节/48hex) AES-256(32字节/64hex)。SM4固定为16字节(32hex)。2. 确认密钥是纯十六进制字符串0-9, a-f且无空格、无0x前缀。解密失败报 “bad decrypt” 或解密后乱码1. 加解密使用的密钥/IV不一致。2. 加密模式或填充方式不匹配。3. 密文在传输或处理中被修改如多余空格、换行。4. 编码问题加密输出是hex解密时却按base64解码。1.最常用在Pre-request和Tests脚本中打印并对比使用的密钥、IV、算法模式是否完全相同。2. 确认双方你的脚本和服务器使用的是同一种模式如CBC和填充如PKCS7。3. 使用console.log输出加密前的明文和加密后的密文与服务器端日志对比。将密文复制到在线的加解密工具仅用于调试用相同参数解密看是否能得到明文。4. 严格统一编码全程使用console.log跟踪数据格式。引入Forge库后脚本执行报未定义错误1. Forge或国密扩展库代码未正确加载。2.eval执行库代码的时机不对。1. 检查“初始化请求”是否成功运行全局变量是否已设置。2. 在集合Pre-request Script最开头添加try { console.log(forge.version); } catch(e) { console.error(‘Forge not loaded’); }来验证。3. 确保库的加载代码在函数定义之前执行。响应解密成功但解析JSON失败1. 解密后的明文并非JSON可能是纯文本或错误信息。2. 解密结果包含不可见字符如BOM头。1. 先console.log解密后的原始字符串肉眼观察。2. 使用pm.environment.set(“debug_resp”, decryptedText)将结果存为变量然后在Postman的“环境”窗口里完整查看。3. 尝试用decryptedText.trim()去除首尾空白符。Postman脚本执行超时1. 引入了过大的库文件导致脚本初始化慢。2. 加解密的数据体量非常大。1. 考虑使用Forge的精简版本或只引入必要的函数。2. 对于大数据考虑是否真的需要在Postman内加解密或许可以简化测试用例。7.2 调试技巧让一切可视化善用console.log()这是Postman脚本调试的生命线。在关键步骤获取密钥、加密前、加密后、解密前、解密后都打印出关键变量的值和类型。利用环境变量暂存中间结果像我们之前做的把last_encrypted_request和last_decrypted_response存起来。你可以在Postman界面右侧的“眼睛”图标环境快速查看或直接打开环境管理器查看它们的完整值。对比验证当你对脚本没把握时找一个公认可靠的在线加解密工具如一些开源项目提供的网页工具作为参照。用相同的明文、密钥、IV、模式对比两者输出的密文是否一致。这是验证你的加密脚本是否正确的最快方法。查看Postman控制台View - Show Postman Console。这里会显示所有网络请求的详细日志和console.log的输出是排查脚本错误和网络问题的利器。7.3 避坑心得从实战中总结密钥IV的格式是万恶之源90%的加解密问题都出在这里。务必确认1字符串是纯十六进制2长度完全正确3没有隐藏的换行符或空格。一个技巧在环境变量里设置密钥时前后加上单引号然后在脚本中用.trim().replace(/’/g, “”)来处理可以避免一些不可见字符问题。不要相信“复制粘贴”从文档、邮件、聊天工具里复制密钥时很容易混入空格或换行。最好让开发人员提供一个可以直接导入Postman环境变量的JSON文件。算法模式要抠字眼接口文档写“AES加密”这信息量几乎为0。必须问清楚是AES-128还是256是CBC还是ECB模式填充是PKCS5Padding还是PKCS7Padding输出是Base64还是Hex同样的SM4也要问清是CBC模式还是ECB模式。先“脱密”调试在联调初期可以请后端开发暂时关闭加密先用明文接口调通业务逻辑。然后再开启加密用你的脚本对接。这样可以隔离问题确定是业务逻辑错误还是加解密错误。封装是为了偷懒但别过度封装对于算法、模式固定的项目高度封装是好事。但如果接口协议经常变过度复杂的封装反而会成为负担。保持脚本一定的可读性和可修改性很重要。