接口签名验证是前后端或者服务端之间对接时的常用安全方案,通过约定好的规则生成签名,可有效防止请求参数被篡改、避免非法请求调用接口。php实现接口签名验证的核心流程围绕参数处理、排序、拼接密钥和hash生成展开,下面逐步讲解具体实现步骤。

接口签名验证的核心逻辑
签名验证的本质是服务端和调用方约定一套相同的签名生成规则,调用方生成签名后随请求传递,服务端按照相同规则重新生成签名,比对两者是否一致,一致则请求合法。核心规则包含三个部分:参数排序规则、密钥拼接方式、hash算法选择。
php实现签名生成的步骤
第一步:获取并过滤请求参数
首先获取所有请求参数,需要过滤掉不参与签名的参数,比如签名本身、文件类型参数等,同时处理参数值为空的情况,通常约定空值不参与签名拼接。
<?php
// 获取所有请求参数,这里以POST请求为例
$params = $_POST;
// 定义不参与签名的参数列表
$excludeParams = ['sign', 'file'];
foreach ($excludeParams as $exclude) {
if (isset($params[$exclude])) {
unset($params[$exclude]);
}
}
// 过滤空值参数,空字符串、null都不参与签名
foreach ($params as $key => $value) {
if ($value === '' || $value === null) {
unset($params[$key]);
}
}
?>
第二步:对参数按键名排序
排序是签名一致性的关键,必须保证调用方和服务端的排序规则完全相同,通常约定按照参数键名的ASCII码从小到大排序,也就是升序排列。
<?php // 按照键名ASCII升序排序 ksort($params); ?>
第三步:拼接参数和密钥
排序完成后,需要将参数拼接成字符串,再拼接约定的密钥。常见的拼接规则是键值对用等号连接,不同参数用&连接,最后拼接上密钥,格式为:key1=value1&key2=value2&key3=value3&secret=密钥。
<?php
// 拼接参数字符串
$str = '';
foreach ($params as $key => $value) {
$str .= $key . '=' . $value . '&';
}
// 去掉末尾多余的&
$str = rtrim($str, '&');
// 拼接密钥,假设密钥为test_secret_123
$secret = 'test_secret_123';
$signStr = $str . '&secret=' . $secret;
?>
第四步:生成hash签名
拼接完成后,使用约定的hash算法生成签名,常用的算法有md5、sha1、sha256等,这里以sha256为例,生成签名后通常会转为小写,避免大小写不一致导致验证失败。
<?php
// 使用sha256算法生成签名
$sign = hash('sha256', $signStr);
// 转为小写
$sign = strtolower($sign);
?>
完整的签名验证流程
生成签名后,调用方需要将签名作为参数传递给服务端,服务端拿到请求参数后,按照上述相同的步骤重新生成签名,和请求携带的签名做比对,一致则验证通过。
<?php
// 服务端验证签名的方法
function verifySign($requestParams, $secret) {
// 过滤不参与签名的参数
$excludeParams = ['sign', 'file'];
$params = $requestParams;
foreach ($excludeParams as $exclude) {
if (isset($params[$exclude])) {
unset($params[$exclude]);
}
}
// 过滤空值
foreach ($params as $key => $value) {
if ($value === '' || $value === null) {
unset($params[$key]);
}
}
// 按键名排序
ksort($params);
// 拼接字符串
$str = '';
foreach ($params as $key => $value) {
$str .= $key . '=' . $value . '&';
}
$str = rtrim($str, '&');
$signStr = $str . '&secret=' . $secret;
// 生成签名
$generateSign = hash('sha256', $signStr);
$generateSign = strtolower($generateSign);
// 比对签名,请求携带的签名为$requestParams['sign']
if (isset($requestParams['sign']) && $generateSign === strtolower($requestParams['sign'])) {
return true;
}
return false;
}
// 使用示例
$requestParams = [
'user_id' => 1001,
'order_id' => 2023001,
'timestamp' => 1690000000,
'sign' => 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3'
];
$secret = 'test_secret_123';
$result = verifySign($requestParams, $secret);
if ($result) {
echo '签名验证通过';
} else {
echo '签名验证失败';
}
?>
注意事项
- 密钥需要严格保密,不要暴露在客户端代码中,建议存储在服务端配置文件里。
- 排序规则、拼接格式、hash算法必须调用方和服务端完全一致,否则签名永远无法匹配。
- 可以在参数中加入时间戳参数,服务端验证签名时同时校验时间戳,避免签名被重复使用,提升安全性。
- 如果参数值包含特殊字符,建议在拼接前做urlencode处理,避免特殊字符影响签名生成。