You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将Axios+crypto请求签名迁移至Postman+CryptoJS遇签名不匹配问题

Postman请求签名不匹配(401错误)问题排查

问题描述

我尝试用Postman调用某API,API文档提供了基于Axios和Node.js crypto的请求签名示例。根据文档要求,X-App-Access-Sig签名需通过SHA256 HMAC算法,使用密钥拼接以下内容生成:

  • 字符串格式的时间戳(即X-App-Access-Ts头的值)
  • 大写HTTP方法
  • 不含主机名的URI(包含查询参数)
  • 请求体(无请求体则不包含)

我按照规则编写了Postman预请求脚本,但调用后返回401错误,提示请求签名不匹配。

API文档示例代码

const axios = require('axios');
const crypto = require('crypto');

var config = {};
config.baseURL= BASE_URL;

axios.interceptors.request.use(createSignature, function (error) {
  return Promise.reject(error);
})

function createSignature(config) {
  console.log('Creating a signature for the request...');

  var ts = Math.floor(Date.now() / 1000);
  const signature = crypto.createHmac('sha256',  SECRET_KEY);
  signature.update(ts + config.method.toUpperCase() + config.url);

  if (config.data instanceof FormData) {
    signature.update (config.data.getBuffer());
  } else if (config.data) {
    signature.update (config.data);
  }

  config.headers['X-App-Access-Ts'] = ts;
  config.headers['X-App-Access-Sig'] = signature.digest('hex');

  return config;
}

我的Postman预请求脚本

var CryptoJs = require("crypto-js")

console.log('Creating a signature for the request...');

const endpoint = pm.request.url.toString().split("{{BASE_URL}}").pop()
var ts = Math.floor(Date.now() / 1000);


const signature = CryptoJs.HmacSHA256(ts + pm.request.method.toUpperCase() + endpoint + pm.request.body, "{{SECRET_KEY}}")

pm.request.headers.add({
    key: 'X-App-Access-Ts',
    value: ts
})
pm.request.headers.add({
    key: 'X-App-Access-Sig',
    value: signature.toString(CryptoJs.enc.Hex)
})

console.log(pm.request)

错误信息

{
    "description": "Request signature mismatch",
    "code": 401,
    "correlationId": "req-53db2067-b0ec-4ce8-b333-387b0b20a955",
    "errorCode": 4003,
    "errorName": "app-token-signature mismatch"
}

问题分析

对比API示例代码和Postman脚本,主要差异点如下:

  1. 请求体处理错误:Postman中直接使用pm.request.body会获取到Postman的Body对象(而非原始请求体字符串/Buffer),导致签名拼接的内容和API要求不符。而Axios示例中会区分FormData和普通请求体,分别处理。
  2. URI获取不可靠:通过split("{{BASE_URL}}")截取URI可能因BASE_URL变量的格式差异(如末尾是否带斜杠)导致截取结果错误。
  3. 时间戳类型隐式转换:虽然JS会自动将数字转为字符串,但显式转换更严谨,避免潜在的类型问题。

修复后的Postman预请求脚本

var CryptoJs = require("crypto-js")

console.log('Creating a signature for the request...');

// 直接获取不含主机的URI(包含查询参数),避免split带来的格式问题
const endpoint = pm.request.url.getPathWithQuery();
// 显式将时间戳转为字符串,和Axios示例保持一致
var ts = Math.floor(Date.now() / 1000).toString();

// 初始化签名原始内容
let signatureRaw = ts + pm.request.method.toUpperCase() + endpoint;

// 根据请求体模式处理请求体内容
if (pm.request.body) {
    switch(pm.request.body.mode) {
        case 'raw':
            // Raw模式直接拼接原始内容
            signatureRaw += pm.request.body.raw;
            break;
        case 'formdata':
            // FormData转为URL编码格式(匹配Axios的FormData.getBuffer()对应的字符串逻辑)
            const formData = pm.request.body.formdata;
            let formStr = '';
            formData.forEach((item, index) => {
                if (index > 0) formStr += '&';
                formStr += `${encodeURIComponent(item.key)}=${encodeURIComponent(item.value)}`;
            });
            signatureRaw += formStr;
            break;
        case 'urlencoded':
            // x-www-form-urlencoded拼接为查询字符串
            const urlEncoded = pm.request.body.urlencoded;
            let urlStr = '';
            urlEncoded.forEach((item, index) => {
                if (index > 0) urlStr += '&';
                urlStr += `${encodeURIComponent(item.key)}=${encodeURIComponent(item.value)}`;
            });
            signatureRaw += urlStr;
            break;
        // 文件上传等其他模式需根据API文档要求另行处理
    }
}

// 计算HMAC SHA256签名
const signature = CryptoJs.HmacSHA256(signatureRaw, "{{SECRET_KEY}}");

// 添加请求头(先移除旧的避免重复)
pm.request.headers.remove('X-App-Access-Ts');
pm.request.headers.remove('X-App-Access-Sig');
pm.request.headers.add({ key: 'X-App-Access-Ts', value: ts });
pm.request.headers.add({ key: 'X-App-Access-Sig', value: signature.toString(CryptoJs.enc.Hex) });

// 打印调试信息,方便对比排查
console.log('签名原始内容:', signatureRaw);
console.log('生成的签名:', signature.toString(CryptoJs.enc.Hex));

修复说明

  • URI获取优化:使用pm.request.url.getPathWithQuery()直接获取符合要求的URI,避免依赖BASE_URL变量的格式。
  • 请求体精准处理:针对Postman不同的请求体模式(raw、formdata、urlencoded)分别处理,确保拼接的请求体内容和Axios示例一致。
  • 避免重复请求头:添加新的签名头前先移除旧的,防止多次执行预请求脚本导致头重复。
  • 调试日志:添加签名原始内容和生成的签名日志,方便对比API示例的签名逻辑,快速定位问题。

内容的提问来源于stack exchange,提问作者sev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 12:15:42