前端请求签名总被后端拒绝,到底哪里出错了?

欧阳自立 阅读 2

我按照后端给的文档对接请求签名,把时间戳、path 和 body 拼起来用 HMAC-SHA256 加密,但每次发请求都返回 403,说签名无效。

我试过用 JSON.stringify(body) 处理请求体,也确认了密钥没错,连 Postman 都能跑通,唯独在浏览器里不行。是不是浏览器自动加了什么头影响了签名?

这是我的签名逻辑:

const sign = (path, body, timestamp) => {
  const secret = 'my_secret_key';
  const payload = <code>${timestamp}n${path}n${JSON.stringify(body)}</code>;
  const encoder = new TextEncoder();
  const key = encoder.encode(secret);
  return crypto.subtle.importKey(
    'raw', key, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
  ).then(k => crypto.subtle.sign('HMAC', k, encoder.encode(payload)))
   .then(sig => Array.from(new Uint8Array(sig)).map(b => b.toString(16).padStart(2, '0')).join(''));
}
我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
迷人的志红
看你的代码,有几个常见坑:

1. JSON.stringify 的顺序问题

这是最可能的原因。对象的属性顺序在不同环境下可能不一致,导致签名结果每次都不一样。建议对 key 排序后再序列化:

const sortedBody = Object.keys(body).sort().reduce((acc, key) => {
acc[key] = body[key];
return acc;
}, {});

const payload = ${timestamp}n${path}n${JSON.stringify(sortedBody)};


2. 空 body 的处理

如果 body 是空对象 {}JSON.stringify({}) 会输出 {},但后端可能期望的是空字符串。你可以加个判断:

const bodyStr = body && Object.keys(body).length > 0 ? JSON.stringify(sortedBody) : '';
const payload = ${timestamp}n${path}n${bodyStr};


3. 调试建议

在浏览器 console 里把你生成的签名和后端能接受的签名对比一下,看看到底哪里不一致。可以在代码里把 payload 打印出来:

console.log('payload:', payload);
console.log('signature:', signature);


然后用 Postman 的结果对比,看看是 payload 部分不同,还是签名算法本身有问题。

4. 时间戳单位

确认一下后端要的是秒还是毫秒,很多坑都在这上面。

你先用这些方式排查一下,重点看 payload 的实际内容和 Postman 发出的是否完全一致。
点赞
2026-03-13 21:14