当前位置: 首页 > news >正文

Postman 前置脚本实战:动态生成接口签名与参数加密

1. 为什么需要动态生成接口签名与参数加密?

当你调用一个需要高安全级别的API时,服务端通常会要求对请求进行签名验证。这就像你去银行办理业务需要出示身份证一样,接口签名就是你的"数字身份证",用来证明这个请求确实是你发出的,而不是别人伪造的。

我遇到过这样一个真实案例:某支付接口要求所有请求参数必须按照特定规则排序后,加上密钥进行MD5加密生成签名。如果手动操作,每次调用接口前都需要:

  1. 把所有参数按字母顺序排列
  2. 拼接成key1=value1&key2=value2的格式
  3. 末尾加上密钥字符串
  4. 对整个字符串做MD5加密
  5. 把加密结果作为sign参数添加到请求中

这个过程不仅繁琐,而且容易出错。更麻烦的是,如果参数有变动,所有步骤都要重来一遍。这就是为什么我们需要在前置脚本中自动化完成这些操作。

2. 前置脚本的工作原理与执行流程

Postman的前置脚本就像是一个智能助手,它会在你点击"Send"按钮后,实际发送请求前自动执行。这个时机非常关键,因为它允许我们在请求最终发出前对内容进行最后的加工和处理。

具体执行顺序是这样的:

  1. 你填写好请求URL、参数和headers
  2. 点击Send按钮
  3. Postman先执行Pre-request Script中的代码
  4. 脚本执行完毕后,Postman将处理后的参数打包
  5. 发送最终的HTTP请求
  6. 收到响应后执行Tests脚本(如果有)

这种机制让我们可以在请求发出前动态修改任何内容,包括URL参数、请求体、headers等。我在实际项目中经常用它来处理各种需要在请求前完成的操作,比如签名生成、参数加密、token刷新等。

3. 实现动态接口签名的完整方案

3.1 准备签名所需的密钥和环境

首先,我们需要安全地管理签名密钥。千万不要把密钥直接写在脚本里!我推荐使用Postman的环境变量来存储:

// 设置环境变量(只需执行一次) pm.environment.set("api_secret", "your_secret_key_here");

在实际项目中,我会为不同环境(开发、测试、生产)设置不同的密钥,通过切换环境来自动使用对应的密钥。

3.2 实现通用签名函数

下面是一个我经过多次优化后的签名函数,支持MD5、SHA1等常见算法:

/** * 生成接口签名 * @param {Object} params 所有请求参数 * @param {String} secret 密钥 * @param {String} algorithm 加密算法,默认MD5 */ function generateSignature(params, secret, algorithm = 'md5') { // 1. 过滤掉sign参数本身和空值参数 const filteredParams = {}; for (const key in params) { if (key !== 'sign' && params[key] !== undefined && params[key] !== '') { filteredParams[key] = params[key]; } } // 2. 按字段名排序 const sortedKeys = Object.keys(filteredParams).sort(); // 3. 拼接键值对 let stringToSign = ''; sortedKeys.forEach(key => { stringToSign += `${key}=${filteredParams[key]}&`; }); // 4. 末尾添加密钥 stringToSign += `key=${secret}`; // 5. 计算签名 const crypto = require('crypto'); const hash = crypto.createHash(algorithm); hash.update(stringToSign); return hash.digest('hex').toUpperCase(); }

3.3 在前置脚本中调用签名函数

有了上面的工具函数,在实际请求中使用就很简单了:

// 获取当前所有请求参数 const requestParams = pm.request.url.query.toObject(); // 如果是POST请求,还需要合并body中的参数 if (pm.request.body && pm.request.body.mode === 'raw') { try { const bodyParams = JSON.parse(pm.request.body.raw); Object.assign(requestParams, bodyParams); } catch (e) { console.log('无法解析JSON body', e); } } // 获取环境变量中的密钥 const secret = pm.environment.get("api_secret"); // 生成签名 const sign = generateSignature(requestParams, secret); // 将签名添加到请求参数中 pm.request.url.query.add({ key: 'sign', value: sign });

4. 参数加密的进阶实现

有些安全性要求更高的接口,不仅需要签名,还需要对参数值本身进行加密。下面介绍几种常见的加密方式在前置脚本中的实现方法。

4.1 AES对称加密

AES是最常用的对称加密算法,适合对敏感数据进行加密:

function aesEncrypt(data, key, iv) { const crypto = require('crypto'); const cipher = crypto.createCipheriv('aes-128-cbc', key, iv); let encrypted = cipher.update(data, 'utf8', 'base64'); encrypted += cipher.final('base64'); return encrypted; } // 使用示例 const encryptedData = aesEncrypt( JSON.stringify({mobile: '13800138000'}), '1234567890123456', // 16位密钥 '1234567890123456' // 16位初始向量 ); pm.environment.set("encrypted_data", encryptedData);

4.2 RSA非对称加密

对于更高级别的安全需求,可以使用RSA非对称加密:

function rsaEncrypt(data, publicKey) { const crypto = require('crypto'); const buffer = Buffer.from(data, 'utf8'); const encrypted = crypto.publicEncrypt({ key: publicKey, padding: crypto.constants.RSA_PKCS1_PADDING }, buffer); return encrypted.toString('base64'); } // 使用示例 const publicKey = `-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC...省略 -----END PUBLIC KEY-----`; const encrypted = rsaEncrypt('敏感数据', publicKey); pm.request.body.raw = JSON.stringify({ encrypted_data: encrypted });

5. 实战中的常见问题与解决方案

在实际使用过程中,我遇到过不少坑,这里分享几个典型问题的解决方法。

5.1 时间戳同步问题

很多接口要求请求必须携带时间戳,且与服务器时间差不能超过一定范围。我们可以这样处理:

// 获取服务器时间(假设有获取服务器时间的接口) pm.sendRequest({ url: pm.environment.get("base_url") + '/api/serverTime', method: 'GET' }, (err, res) => { if (!err) { const serverTime = res.json().data.timestamp; const localTime = Date.now(); const timeDiff = Math.abs(localTime - serverTime); // 如果时间差超过5分钟,使用服务器时间 const finalTimestamp = timeDiff > 300000 ? serverTime : localTime; pm.environment.set("current_timestamp", finalTimestamp); } });

5.2 参数编码问题

不同服务器对参数编码的处理可能不同,我建议在前置脚本中统一处理:

function normalizeParams(params) { const normalized = {}; for (const key in params) { if (params[key] !== undefined && params[key] !== null) { // 统一转为字符串并编码 normalized[key] = encodeURIComponent(params[key].toString()); } } return normalized; }

5.3 调试技巧

调试前置脚本时,这些技巧很有帮助:

  1. 多用console.log()输出中间结果
  2. 使用Postman的Console(View → Show Postman Console)
  3. 对于异步操作,确保所有关键步骤都有日志
  4. 可以使用pm.prompt()弹出重要变量的值
// 调试示例 console.log('原始参数:', requestParams); console.log('排序后的参数字符串:', stringToSign); console.log('生成的签名:', sign); // 或者弹出重要信息 pm.prompt("签名生成结果", `签名: ${sign}\n用于签名的字符串: ${stringToSign}`);

6. 高级应用场景

除了基本的签名和加密,前置脚本还能实现很多高级功能。

6.1 自动刷新Token

对于需要定期刷新的access token,可以这样处理:

// 检查token是否即将过期 const tokenExpire = pm.environment.get("token_expire"); if (tokenExpire && new Date(tokenExpire) < new Date(Date.now() + 300000)) { // 提前5分钟刷新token pm.sendRequest({ url: pm.environment.get("auth_url"), method: 'POST', body: { mode: 'raw', raw: JSON.stringify({ refresh_token: pm.environment.get("refresh_token") }) } }, (err, res) => { if (!err && res.code === 200) { const data = res.json(); pm.environment.set("access_token", data.access_token); pm.environment.set("token_expire", new Date(Date.now() + data.expires_in * 1000)); } }); }

6.2 请求参数校验

可以在发送请求前校验参数合法性:

// 校验手机号格式 function validateMobile(mobile) { return /^1[3-9]\d{9}$/.test(mobile); } const mobile = pm.request.body.urlencoded.get('mobile'); if (!validateMobile(mobile)) { pm.prompt("参数错误", "手机号格式不正确"); // 阻止请求发送 pm.environment.set("should_abort", "true"); } // 在Tests脚本中检查是否应该中止 if (pm.environment.get("should_abort") === "true") { pm.environment.unset("should_abort"); pm.test("请求被中止", () => {}); throw new Error("请求因参数校验失败被中止"); }

6.3 批量请求处理

对于需要批量发送的请求,可以动态生成参数:

// 生成批量测试数据 const testCases = []; for (let i = 0; i < 5; i++) { testCases.push({ userId: `test_${Math.random().toString(36).substr(2, 8)}`, amount: Math.floor(Math.random() * 1000) + 1 }); } // 存入环境变量供后续使用 pm.environment.set("test_cases", JSON.stringify(testCases));
http://www.jsqmd.com/news/518519/

相关文章:

  • 手机拍照也能玩高光谱?教你用TensorFlow Lite在Android上实现实时RGB转高光谱
  • BasicsLibrary:面向嵌入式初学者的Arduino零门槛硬件交互库
  • 基于YOLOv8/YOLOv10/YOLOv11/YOLOv12与SpringBoot的小麦叶片病害检测系统(DeepSeek智能分析+web交互界面+前后端分离+YOLO数据)
  • BEVFusion实战:如何在nuScenes数据集上快速搭建3D目标检测环境(附常见报错解决方案)
  • Audacity隐藏技巧:用Python脚本批量拆分100+音频文件(Windows/Mac通用)
  • 直齿轮和斜齿轮啮合刚度计算Matlab程序
  • 别再让LLM‘盲猜’了!用MCP Server给你的Java后端开个‘数据接口’
  • Windows平台打造极速Verilog/SystemVerilog开发环境:从零配置到高效编码
  • Altium Designer vs 立创EDA:跨平台封装迁移的3个隐藏技巧
  • 基于YOLOv8/YOLOv10/YOLOv11/YOLOv12与SpringBoot的白细胞类型检测系统(DeepSeek智能分析+web交互界面+前后端分离+YOLO数据)
  • 中国芯片出口额暴涨七成,芯片单价猛涨五成,苦熬终获巨额回报
  • 别再死记公式了!用MATLAB Simulink手把手复现PMSM的Clark变换(附模型文件)
  • 基于YOLOv8/YOLOv10/YOLOv11/YOLOv12与SpringBoot的水稻病害检测系统(DeepSeek智能分析+web交互界面+前后端分离+YOLO数据)
  • 基于MATLAB的三相异步电机矢量控制变频调速系统设计 本设计包括设计报告,仿真程序
  • 【2026最新 】Dev C++官网下载安装图文教程:免费C/C++开发环境超详细使用指南 - xiema
  • 欧拉Euler 21.10系统下OpenSSH 9.0升级全攻略:从依赖安装到systemctl异常处理
  • ES8311寄存器深度解析与实战优化笔记
  • 告别复杂配置:零基础玩转文本驱动目标检测
  • Excel+VBA批量提取PDF文字:5分钟搞定合同数据整理(附完整代码)
  • 机器学习期末复习:从判别式模型到生成式模型的实战解析(附典型算法对比)
  • 解锁服务器潜能:10个创意项目让你的云端生活更酷
  • Factory Droid Cli安装
  • Oh-My-OpenCode介绍
  • 高云FPGA开发避坑指南:从FIFO实现到资源优化实战
  • 从零到一:3D Slicer心脏CT影像分割与标签制作实战
  • 电压型虚拟同步发电机(VSG)离网仿真模型及三相并离网切换VSG预同步控制
  • ASF-YOLO实战:如何用YOLOv5改进模型搞定细胞分割(附代码)
  • 避坑指南:BIOS设置里那些影响整机性能的关键选项(含性能测试对比数据)
  • JS如何基于WebUploader实现军工涉密图纸的浏览器端分片加密断点续传与审计?
  • ESP8266连网后,除了NTP还能玩点啥?用Arduino NTPClient库做个智能时钟(附完整代码)