HMAC签名在前端加密时,为什么服务端验证总是失败?

UX俊杰 阅读 78

我在给登录接口加HMAC签名验证时遇到了问题。按照文档用CryptoJS生成签名,把时间戳和参数按字母排序后拼接,但服务端一直返回”签名无效”。试过确认密钥和算法都正确,连请求头的Content-Type都按服务端要求改成了application/json,还是不行:


const params = JSON.stringify({
  timestamp: Date.now(),
  username: 'test',
  password: '123'
});
const hmac = CryptoJS.HmacSHA256(params, 'secret-key').toString();

服务端提示签名计算方式有问题,难道是参数排序规则或者时间戳格式哪里没对齐?或者加密前的字符串处理漏了什么步骤?

我来解答 赞 15 收藏
二维码
手机扫码查看
2 条解答
UI淑宁
UI淑宁 Lv1
你前端加密的是 JSON 字符串,但服务端很可能对原始参数对象排序后拼接成 query string 再签名的,两边不一致当然验不过。懒人方案:别自己拼字符串,让前后端用同样的规范化方法构造待签文字。

const params = {
timestamp: Date.now(),
username: 'test',
password: '123'
};

// 按 key 字典序排序并拼成 a=1&b=2 格式,注意这里不要 encode 而是留给签名前再整体处理
const sortedString = Object.keys(params).sort().map(k => ${k}=${params[k]}).join('&');
const hmac = CryptoJS.HmacSHA256(sortedString, 'secret-key').toString(CryptoJS.enc.Hex);


先确认服务端是怎么拼接参数的,跟着它做就完事了。
点赞 5
2026-02-09 16:04
艺童
艺童 Lv1
我之前也踩过这个坑。问题出在签名字符串的拼接规则上。服务端在验证HMAC签名时,需要严格遵循相同的拼接格式,否则一定会失败。

关键点在于:签名字符串的构造规则必须和服务端保持一致。比如:

时间戳是否要求为10位或13位?
参数是否要进行排序?排序方式是按key的ASCII还是其他?
是否需要拼接方式是key1=value1&key2=value2还是key1value1key2value2或者其他?
是否对参数值进行了encode处理?

从你贴的代码看,你是直接对整个params对象进行了stringify,然后拿这个字符串去签名。但服务端可能压根不是这么拼的。

我猜你服务端那边的签名逻辑大概是这样处理的:

把参数按key排序
拼接成key=value的形式,然后用&连接起来
最后加上密钥进行HMAC

所以你的前端代码应该重构为:

const params = {
timestamp: Date.now(),
username: 'test',
password: '123'
};

const sortedKeys = Object.keys(params).sort();
const strToSign = sortedKeys.map(k => ${k}=${params[k]}).join('&');
const hmac = CryptoJS.HmacSHA256(strToSign, 'secret-key').toString();


这样拼出来的字符串,才和服务端预期的签名原文一致。

建议你和服务端同学对齐签名字符串的拼接规则,包括是否对参数做encode,顺序、格式这些细节。
点赞 11
2026-02-05 06:04