前端请求签名总被后端拒绝,到底哪里出错了?
我按照后端给的文档对接请求签名,把时间戳、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. JSON.stringify 的顺序问题
这是最可能的原因。对象的属性顺序在不同环境下可能不一致,导致签名结果每次都不一样。建议对 key 排序后再序列化:
2. 空 body 的处理
如果 body 是空对象
{},JSON.stringify({})会输出{},但后端可能期望的是空字符串。你可以加个判断:3. 调试建议
在浏览器 console 里把你生成的签名和后端能接受的签名对比一下,看看到底哪里不一致。可以在代码里把 payload 打印出来:
然后用 Postman 的结果对比,看看是 payload 部分不同,还是签名算法本身有问题。
4. 时间戳单位
确认一下后端要的是秒还是毫秒,很多坑都在这上面。
你先用这些方式排查一下,重点看 payload 的实际内容和 Postman 发出的是否完全一致。