签名校验失败的核心原因是两端签名逻辑不一致,需严格统一参数排序(uksort+小写键)、清洗空值、标准化拼接(http_build_query)、密钥处理(trim+hash_hmac)及签名输出格式。

签名校验失败,核心问题几乎都出在“两端生成签名的逻辑不一致”。不是算法写错了,而是参数处理、拼接顺序、编码方式这些细节没对齐。直接比对原始拼串,就能快速定位。
参数排序必须强制 ASCII 字节序
PHP 的 ksort() 在遇到数字键、大小写混排或中文键时行为不稳定,容易导致客户端和服务端排序结果不同。不能依赖默认排序。
- 用
uksort($params, 'strcmp')替代ksort(),确保按字节严格升序 - 所有键名统一转小写再排序(如
$sorted = array_change_key_case($params, CASE_LOWER)),避免大小写干扰 - 排序前务必
unset($params['sign']),且不能遗漏其他非参与签名字段(如callback、format)
空值和无效参数要彻底过滤
客户端传了 page=2&keyword=&category=1,服务端若把空字符串 '' 当作有效值参与签名,而客户端实际跳过了它,就会不一致。
- 用
array_filter($params, function($v) { return $v !== null && $v !== '' && $v !== 'null'; })清洗参数 - 注意:
0、false、'0'是有效值,不能误删;只剔除真正无意义的空态 - 调试时打印清洗后的参数数组,确认两边结构完全一致
拼接字符串必须标准化且可复现
手拼 "a=".$a."&b=".$b 容易漏空格、多&、中文未编码;http_build_query 是更安全的选择,但要用对参数。
立即学习“PHP免费学习笔记(深入)”;
- 固定用
http_build_query($sorted_params, '', '&', PHP_QUERY_RFC3986),避免 URL 编码差异 - 密钥拼接位置要明确:是追加在 query string 末尾(
$str . $secret),还是插入中间?文档怎么写就怎么来 - JSON body 参与签名时,必须用
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),禁用换行和空格
密钥和哈希算法必须严丝合缝
密钥哪怕多一个空格、少一个字符,或用了 base64 解码却没配对,签名就全错。SHA256 比 MD5 更安全,但两端必须用同一个函数。
- 密钥读取后立即
trim(),并检查长度(SHA256 建议 ≥32 字节) - 必须用
hash_hmac('sha256', $data, $secret),不是hash('hmac-sha256', ...)(该函数不存在) - 签名结果统一用
bin2hex()转小写十六进制,不要strtoupper()或 base64 - 记录完整待签名字符串(含密钥)、原始参数、最终 sign,三者日志一起查



















