在 Postman 中实现 API 请求的自动化签名与时间戳更新
详细讲解一个功能完备的 Postman 预请求脚本,该脚本可以实现以下自动化功能:
自动更新时间戳:如果请求体中存在 Timestamp 字段(不区分大小写),则自动更新为当前的 Unix 时间戳。
条件化签名:仅当请求体中存在 sign 字段(不区分大小写)时,才触发签名计算逻辑。
精确的签名算法:
排除 sign 字段、null 值及空字符串。
对所有参与签名的参数按键名进行不区分大小写的字母排序。
将排序后的参数拼接成 key=value 的字符串,并用 & 连接。
在末尾追加 &appkey=...。
使用 MD5 算法生成最终签名。
保持大小写:脚本能够智能识别并保留请求体中 sign 和 Timestamp 字段的原始大小写格式。
准备工作
在开始之前,请完成以下两个简单的配置步骤。
步骤一:设置环境变量
为了安全和方便地管理你的密钥(appKey),建议将其存储在 Postman 的环境变量中。
点击 Postman 界面右上角的眼睛图标,选择 "Edit" 或 "Add" 来编辑当前环境。
添加一个新的变量:
VARIABLE: appKey
CURRENT VALUE: 填入你的实际密钥
步骤二:配置 Postman 请求
创建一个 POST 请求。
在 Headers 标签页,确保 Content-Type 的值为 application/json。
在 Body 标签页,选择 raw 并从下拉菜单中选择 JSON。在这里输入你的原始 JSON 请求数据。
最终的 Pre-request Script
将以下完整的 JavaScript 代码粘贴到你的 Postman 请求的 Pre-request Script 标签页中。
code JavaScript
downloadcontent_copy
expand_less
// 引入 Postman 内置的 CryptoJS 库
const CryptoJS = require('crypto-js');
// 1. 获取并解析原始 JSON 请求体
let bodyObject;
const rawBody = pm.request.body.raw;
if (!rawBody || rawBody.trim() === "") {
console.log("请求体为空,跳过所有处理。");
return;
}
try {
bodyObject = JSON.parse(rawBody);
} catch (e) {
console.error("JSON 解析失败:", e);
throw new Error("Invalid JSON body.");
}
// 2. 自动更新时间戳(如果不区分大小写地存在)
// 首先查找 'timestamp' 键的原始大小写形式 (e.g., 'timestamp', 'Timestamp')
const timestampKey = Object.keys(bodyObject).find(key => key.toLowerCase() === 'timestamp');
if (timestampKey) {
// 获取当前时间的 Unix 时间戳 (以秒为单位)
const currentTimestamp = Math.floor(Date.now() / 1000);
// 使用原始键名更新 body 对象中的时间戳
bodyObject[timestampKey] = currentTimestamp;
console.log(`找到时间戳字段 '${timestampKey}',已更新为当前时间: ${currentTimestamp}`);
}
// 3. 条件化签名:检查是否存在 'sign' 字段
// 查找 'sign' 键的原始大小写形式 (e.g., 'sign', 'Sign')
const signKey = Object.keys(bodyObject).find(key => key.toLowerCase() === 'sign');
// 如果未找到 'sign' 键,则认为不需要签名
if (!signKey) {
console.log("请求体中未找到 'sign' 字段,跳过签名计算。");
// 即使不签名,也要确保已更新的时间戳被应用到最终请求中
if (timestampKey) {
pm.request.body.raw = JSON.stringify(bodyObject, null, 2);
console.log("已更新请求体中的时间戳。");
}
return; // 退出脚本,不执行签名流程
}
// --- 如果找到了 'sign' 键,则执行完整的签名流程 ---
console.log(`找到签名字段 '${signKey}',开始执行签名计算...`);
// 从环境变量中获取 appKey
const appKey = pm.environment.get("appKey");
if (!appKey) {
console.error("错误: 'appKey' 环境变量未设置。");
throw new Error("appKey is not defined in the environment.");
}
// 4. 过滤参数 (此时 Timestamp 可能已被更新)
const filteredParams = {};
for (const key in bodyObject) {
if (Object.prototype.hasOwnProperty.call(bodyObject, key)) {
// 不区分大小写地排除 'sign' 字段
if (key.toLowerCase() === 'sign') {
continue;
}
const value = bodyObject[key];
// 排除 null 和空字符串
if (value !== null && value !== "") {
filteredParams[key] = value;
}
}
}
// 5. 按 key 的字母顺序(不区分大小写)进行排序
const sortedKeys = Object.keys(filteredParams).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
// 6. 拼接成 key=value&key=value 的格式
const paramPairs = sortedKeys.map(key => {
let value = filteredParams[key];
if (typeof value === 'object' && value !== null) {
value = JSON.stringify(value);
}
return `${key}=${value}`;
});
const deeplinkString = paramPairs.join('&');
// 7. 追加 appkey 构造待签名字符串
let stringToSign;
if (deeplinkString) {
stringToSign = `${deeplinkString}&appkey=${appKey}`;
} else {
stringToSign = `appkey=${appKey}`;
}
// 8. 计算 MD5 签名
const sign = CryptoJS.MD5(stringToSign).toString();
// 9. 使用原始键名将新签名回填到 body 对象
bodyObject[signKey] = sign;
// 10. 更新请求体,此时会同时包含最新的时间和签名
pm.request.body.raw = JSON.stringify(bodyObject, null, 2);
console.log("签名计算完成,已更新请求体中的时间和签名。");
Comments