使用HTTP消息体sign签名,签名规则如下:
- 只使用data值进行签名
- 签名特别注意以下重要规则
1.参数名从小到大排序(字典序);
2.如果参数的值为NULL则不参与签名;
3.参数名区分大小写;
4.平台接口可能增加字段,验证签名时必须支持增加的扩展字段
签名举例
第一步
设所有发送或者接收到的请求数据为集合 M,将集合 M 内非空参数值的参数按照参数名从小到大(字典序)
排序,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串String A第二步
在String A 最后拼接上appSecret(即 key1=value1&key2=value2&…&key=appSecret ),得到stringSignTemp字符串假设appid与appSecret参数为:
appId = qqesdadawasddwaw1
appSecret = 123456789aaa假设传送data参数如下:
{ "deviceNo":"696db22f7a57e7f2111", "account":"12345678", "eventNo":"2024DE1726016101142207", "timeStamp":1726803917 }
1:对参数按照key=value的格式,并按照参数名字典序排序如下(在拼接之前需对每个value进行转码,转为UTF-8)
string A = account=12345678&deviceNo=696db22f7a57e7f2111&eventNo=2024DE1726016101142207&timeStamp=1726803917&
2:再拼接上key=appSecret
string stringSignTemp = account=12345678&deviceNo=696db22f7a57e7f2111&eventNo=2024DE1726016101142207&timeStamp=1726803917&key=123456789aaa
第三步
对stringSignTemp进行标准MD5 32位运算,得到sign值sign=MD5(stringSignTamp).toUpperCase()=”7C427163D878947E94D05DF7F30FD185” //注:MD5签名方式
得到最终发送数据
{ "data": { "deviceNo":"696db22f7a57e7f2111", "account":"12345678", "eventNo":"2024DE1726016101142207", "timeStamp":1726803917 }, "appId":"qqesdadawasddwaw1", "sign":"7C427163D878947E94D05DF7F30FD185" }
对于含有嵌套的对象也需要排序,如果数组是字符串,因为没有键值,保持顺序即可,不用排序,
data 嵌套实例;
{
"aa": "123",
"UU": 45,
"data": {
"name": "",
"planNo": {
"a1": "c",
"z1": "",
"c1": ""
},
"test": [
"bb",
"zz",
"ee"
],
"b": "hello",
"uid": "17496",
"url": "https:"
}
}
public static void main(String[] args) {
String dataJson = "{\"aa\":\"123\",\"UU\":45,\"data\":{\"name\":\"\",\"planNo\":{\"a1\":\"c\",\"z1\":\"\",\"c1\":\"\"},\"test\":[\"bb\",\"zz\",\"ee\"],\"b\":\"hello\",\"uid\":\"17496\",\"url\":\"https:\"}}";
String sign = getSign(dataJson, "343434343434343434", /*System.currentTimeMillis() / 1000*/ 1749887069);
System.out.println("sign:" + sign);
}
public static String getSign(String json, String appSecret,long timestamp){
JSONObject jsonObject = JSON.parseObject(json);
jsonObject.put("timestamp", timestamp);
JSONObject parse = sortJsonKeys(jsonObject);
String result = parse.keySet()
.stream().filter(key -> !"sign".equalsIgnoreCase(key))
.filter(key -> parse.getString(key) != null)//空字符参数签名
.map(key -> {
try {
// return key + "=" + parse.getString(key);
return key + "=" + URLEncoder.encode(parse.getString(key), StandardCharsets.UTF_8.name());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}).reduce("", (a, b) -> a + "&" + b).substring(1);
System.out.println("result:" + result);
String signSrc = result + "&key=" + appSecret;
System.out.println("signSrc:" + signSrc);
return DigestUtils.md5Hex(signSrc).toUpperCase();
}
/**
* 注意:test数组的元素顺序保持不变,因为数组排序通常不是按元素内容排序,
* 而是保持其插入顺序。如果需要对数组内的对象进行排序,可以根据对象的某个属性来实现自定义排序逻辑。然而,在本例中,test数组的元素是字符串,因此无需进一步排序。
* @param json
* @return
*/
public static JSONObject sortJsonKeys(JSONObject json) {
TreeMap<String, Object> sortedMap = new TreeMap<>();
json.forEach((key, value) -> {
if (value instanceof JSONObject) {
sortedMap.put(key, sortJsonKeys((JSONObject) value));
} else if (value instanceof JSONArray) {
// 对于数组,我们保持其顺序不变,但递归处理数组内的对象
JSONArray sortedArray = new JSONArray();
for (Object element : (JSONArray) value) {
if (element instanceof JSONObject) {
sortedArray.add(sortJsonKeys((JSONObject) element));
} else {
sortedArray.add(element);
}
}
sortedMap.put(key, sortedArray);
} else {
sortedMap.put(key, value);
}
});
return new JSONObject(sortedMap);
}
为了方便查看,没有url编码前的数据:
result:UU=45&aa=123&data={"b":"hello","name":"","planNo":{"a1":"c","c1":"","z1":""},"test":["bb","zz","ee"],"uid":"17496","url":"https:"}×tamp=1749887069
signSrc:UU=45&aa=123&data={"b":"hello","name":"","planNo":{"a1":"c","c1":"","z1":""},"test":["bb","zz","ee"],"uid":"17496","url":"https:"}×tamp=1749887069&key=343434343434343434
sign:FEB25D95FFDD0FC5F4BE753C7E1AE4FD
作者:邹文韬 创建时间:2025-06-07 15:13
最后编辑:吴勇 更新时间:2025-06-20 15:49
最后编辑:吴勇 更新时间:2025-06-20 15:49