| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- <?php
- namespace common\components\delivery\helpers;
- class SignHelper
- {
- /**
- * 通用签名算法入口
- * @param array $params 待签名参数
- * @param string $secret 密钥
- * @param string $method 签名算法:md5 / hmac_sha256
- * @param bool $sort 是否需要按 key 排序
- * @param string $platform 默认通用(common),其它请用平台名
- */
- public static function makeSign(array $params, string $secret, string $method = 'md5', bool $sort = true, string $platform = 'common')
- {
- if ($sort) {
- ksort($params);
- }
- // 拼接 query 字符串(过滤空值)
- $str = '';
- foreach ($params as $k => $v) {
- if (is_array($v)) {
- // 关键:在 JSON 编码前对数组进行递归排序
- //$v = self::sortArrayRecursive($v);
- $v = json_encode($v, JSON_UNESCAPED_UNICODE);
- }
- if ($platform == 'shansong') {
- $str .= "{$k}{$v}";
- } else {
- $str .= "{$k}={$v}&";
- }
- }
- $str = rtrim($str, '&');
- switch (strtolower($method)) {
- case 'hmac_sha256':
- $sign = hash_hmac('sha256', $str, $secret);
- break;
- case 'sha256':
- if ($platform == 'fengniao') {
- $sign = $secret . $str;
- } else {
- $sign = $str . $secret;
- }
- $sign = hash('sha256', $sign);
- break;
- case 'md5':
- default:
- if ($platform == 'shansong') {
- $sign = $secret . $str;
- } else {
- $sign = $str . $secret;
- }
- $sign = md5($sign);
- break;
- }
- return strtoupper($sign);
- }
- /**
- * 验证签名(用于第三方回调)
- */
- public static function verify(array $params, string $secret, string $signKey = 'sign', string $method = 'md5')
- {
- if (!isset($params[$signKey])) return false;
- $sign = $params[$signKey];
- unset($params[$signKey]);
- $expect = self::makeSign($params, $secret, $method);
- return strtoupper($sign) === strtoupper($expect);
- }
- /**
- * 递归过滤数组中的空值(null、空字符串、空数组)
- * 确保签名时不包含空值参数
- *
- * @param array $data 需要过滤的数组
- * @return array 过滤后的数组
- */
- private static function filterEmptyValues($data)
- {
- $filtered = [];
- foreach ($data as $key => $value) {
- // 过滤掉 null、空字符串、空数组
- if ($value === null || $value === '' || (is_array($value) && empty($value))) {
- continue;
- }
-
- // 如果是数组,递归处理
- if (is_array($value)) {
- $value = self::filterEmptyValues($value);
- // 递归过滤后如果变成空数组,也要跳过
- if (empty($value)) {
- continue;
- }
- }
-
- $filtered[$key] = $value;
- }
- return $filtered;
- }
- /**
- * 递归对数组进行排序(包括嵌套数组)
- * 确保 JSON 编码时键的顺序一致
- *
- * @param array $arr 需要排序的数组
- * @return array 排序后的数组
- */
- private static function sortArrayRecursive($arr)
- {
- if (!is_array($arr)) {
- return $arr;
- }
-
- // 对当前数组进行排序
- ksort($arr);
-
- // 递归处理嵌套数组
- foreach ($arr as $key => $value) {
- if (is_array($value)) {
- $arr[$key] = self::sortArrayRecursive($value);
- }
- }
-
- return $arr;
- }
- }
|