概述

该文档提供了对停车场开放接口请求的签名逻辑实现的详细说明。签名逻辑主要包括对请求参数的处理、排序以及使用 HMAC-SHA256 进行签名的步骤

签名生成步骤

  1. 准备数据:准备需要参与签名的数据,包括AppIdParkKeyTimeStampNonceData

  2. 处理Data:将Data进行ASCII码从小到大排序(升序)后转换为字符串(转换小写),再进行MD5加密(32模式,UTF-8编码)。具体步骤如下:

    • 如果Data存在数组或集合,将数组或集合转换为列表,对列表中的每个元素进行处理,然后使用&符号连接起来。
    • 如果Data是对象,则直接对该对象的所有字段进行排序,使用&符号将字段和值连接起来。

注意:Data为Null时也要参与签名,转为Data=,其他参数为Null不参与签名

  1. 生成签名字符串:将AppIdData(已经处理过的)、ParkKeyTimeStampNonce按照这个顺序,使用&符号连接起来,形成一个字符串。

  2. 签名:将上一步生成的字符串转换为小写,然后使用HMAC-SHA256算法进行签名(签名结果转换成base64小写),密钥为AppSecret

示例

假设我们有以下数据:

{
    "ParkKey":"1702399313365243",
    "AppId":"fkqju0n1",
    "TimeStamp":"1704527859009",
    "Nonce":"928885",
    "Sign":"x1aeqmo3/hs9cbhlbnzburgskf1gvd/lil2t/iasiyo=",
    "Data":[
        {
            "Index":1,
            "Url":"http://aebhfqld.gt/tgmjt"
        },
        {
            "Index":3,
            "Url":"http://qtdupkzq.sd/dsicxsc"
        },
        {
            "Url":"http://dfxkmte.tv/mpeop",
            "Index":2
        },
        {
            "Url":"http://hdphhisif.ni/uejufkem",
            "Index":4
        }
    ]
}

首先,我们需要处理Data。我们将Data的键值对按照ASCII码从小到大排序,得到:

Index=1&Url=http://aebhfqld.gt/tgmjt&Index=2&Url=http://dfxkmte.tv/mpeop&Index=3&Url=http://qtdupkzq.sd/dsicxsc&Index=4&Url=http://hdphhisif.ni/uejufkem

然后,我们对这个字符串转换成小写后进行MD5加密,假设得到的结果为md5Result

然后,我们生成签名字符串:

AppId=myAppId&Data=md5Result&ParkKey=myParkKey&TimeStamp=1632739200000&Nonce=myNonce

最后,我们将这个字符串转换为小写,然后使用HMAC-SHA256算法进行签名,密钥为myAppSecret,得到的结果就是我们需要的签名。

注意事项

  • 所有的键值对都需要按照ASCII码从小到大排序。
  • HMAC-SHA256签名时,密钥应为AppSecret
  • 时间戳TimeStamp应为13位的Unix时间戳(毫秒级)。
  • Nonce应为随机字符串,用于防止重复请求。
  • 其中DataNull时也要参与签名,其他字段为Null不参与签名

相关代码示例

相关代码

HMAC-SHA256签名方法实现

    /// <summary>
    /// HMAC-SHA256 签名
    /// </summary>
    /// <param name="data">待签名数据</param>
    /// <param name="secret">密钥</param>
    /// <returns>签名后的数据, base64编码,转换成小写</returns>
    public static string HmacSha256Sign(string data, string secret)
    {
        try
        {
            var encoding = new UTF8Encoding();
            var keyByte = encoding.GetBytes(secret);
            var messageBytes = encoding.GetBytes(data);
            using var hmacsha256 = new System.Security.Cryptography.HMACSHA256(keyByte);
            var hashmessage = hmacsha256.ComputeHash(messageBytes);
            var result = Convert.ToBase64String(hashmessage);
            return result.ToLower();
        }
        catch (Exception ex)
        {
            OpenApiLog.WriteLog("HMAC-SHA256 签名异常", ex);
            return string.Empty;
        }
    }

Data对象排序成字符串方法实现

    /// <summary>
    /// 将Data进行ASCII码从小到大排序(升序)后转换为字符串
    /// </summary>
    /// <param name="data">请求数据</param>
    /// <returns>转换后的字符串</returns>
    private static string ConvertDataToString(object data)
    {
        // 如果数据为空,返回空字符串
        if (data == null)
        {
            return string.Empty;
        }

        // 如果数据是数组或者集合
        if (data.GetType().IsArray || data is IEnumerable<object>)
        {
            // 将数组转换为列表
            var dataList = ((IEnumerable<object>)data).ToList();
            // 使用递归的方式对列表中的每个元素进行处理
            var dataDictList = dataList.Select(ConvertDataToString);
            // 对集合使用稳定排序,保证相同的元素不会交换位置
            dataDictList = dataDictList.OrderBy(p => p, StringComparer.Ordinal).ToList();
            // 将每个元素转换为一个字符串,并使用&符号连接起来
            return string.Join("&", dataDictList);
        }

        // 如果数据是对象
        // 使用反射获取对象的属性值,并将其转换为一个键值对序列
        var dataDict = data.GetType().GetProperties()
            .Select(p =>
            {
                // 获取属性值
                var value = p.GetValue(data);
                // 如果属性值是数组,使用递归的方式进行处理
                if (value != null && (value.GetType().IsArray || value is IEnumerable<object>))
                {
                    value = ConvertDataToString(value);
                }
                // 返回键值对
                return new KeyValuePair<string, object>(p.Name, value);
            })
            // 过滤掉值为null的键值对
            .Where(p => p.Value != null)
            // 对键值对序列使用稳定排序
            .OrderBy(p => p.Key, StringComparer.Ordinal)
            // 将键值对序列转换为字典
            .ToDictionary(p => p.Key, p => p.Value);

        // 将字典转换为一个字符串,并使用&符号连接起来
        return string.Join("&", dataDict.Select(p => $"{p.Key}={p.Value}"));
    }
作者:mry  创建时间:2024-10-24 09:01
 更新时间:2024-10-24 11:29