• 使用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:"}&timestamp=1749887069
signSrc:UU=45&aa=123&data={"b":"hello","name":"","planNo":{"a1":"c","c1":"","z1":""},"test":["bb","zz","ee"],"uid":"17496","url":"https:"}&timestamp=1749887069&key=343434343434343434
sign:FEB25D95FFDD0FC5F4BE753C7E1AE4FD
作者:邹文韬  创建时间:2025-06-07 15:13
最后编辑:吴勇  更新时间:2025-06-20 15:49