go map 按字典升序排序后转 JSON md5 与 PHP 不一致

2020-06-14 13:21:54 +08:00
 Nick66

源码如下

package main

import (
    "fmt"
    "sort"
    "encoding/json"
    "crypto/md5"
    "encoding/hex"
    "bytes"
)

func main() {

    params := make(map[string]string)
    params["name"] = "111"
    params["domain"] = "www.baidu.com?name=1&id=1"

    // 排序
    keys := make([]string, len(params))
    i := 0
    for k, _ := range params {
        keys[i] = k
        i++
    }
    sort.Strings(keys)

    for _, k := range keys {
        fmt.Println(k)
    }

    byteBuf := bytes.NewBuffer([]byte{})
    encoder := json.NewEncoder(byteBuf)
    // 特殊字符不转义
    encoder.SetEscapeHTML(false)
    err := encoder.Encode(params)
    if err != nil {
        panic(err)
    }
    data := byteBuf.String()
    fmt.Println(data)
    h := md5.New()
    // 与 php 一致
    h.Write([]byte(`{"domain":"www.baidu.com?name=1&id=1","name":"111"}`))
    // 不一致
    // h.Write([]byte(data))
    fmt.Println(hex.EncodeToString(h.Sum(nil)))
    // php 结果为 06820973e7b8d3acdb4763264a803620

}
2473 次点击
所在节点    程序员
12 条回复
also24
2020-06-14 13:27:38 +08:00
说个题外话:
也不知道谁带起的这种风气,用这种方式来进行检验。

JSON 序列化的时候,是否换行,是否缩进,是否转义都会造成序列化结果字符串的差异。

正确的做法是直接传输序列化后的 JSON String,计算 hash 的时候也直接针对整个 JSON String
Nick66
2020-06-14 13:31:16 +08:00
@also24 确实是这样,现在这个功能就为了把参数 md5 之后作为签名参数用
marquina
2020-06-14 13:34:09 +08:00
json 在我这个 pythoner 眼里就是一个 dict,我也从来不会对 json 序列化、反序列化时 key 的顺序有任何期待,更别提算 md5 这种操作了
Nick66
2020-06-14 13:35:32 +08:00
@marquina 接手项目的时候就是这样,我也无法改变,只能想办法解决。
haiyang416
2020-06-14 13:51:19 +08:00
chinvo
2020-06-14 13:55:10 +08:00
签名的正确玩法是对 http body 签名,签名结果放到 header 里

这样就不涉及排序问题
Nick66
2020-06-14 14:01:37 +08:00
@haiyang416 感谢,解决了。
Vegetable
2020-06-14 14:02:36 +08:00
byteBuf.Bytes()
[123 34 100 111 109 97 105 110 34 58 34 119 119 119 46 98 97 105 100 117 46 99 111 109 63 110 97 109 101 61 49 38 105 100 61 49 34 44 34 110 97 109 101 34 58 34 49 49 49 34 125 10]

最后多了一个 0010,换行符。trim 掉就一样了
h.Write(bytes.TrimSpace(byteBuf.Bytes()))
fmt.Println(hex.EncodeToString(h.Sum(nil)))
06820973e7b8d3acdb4763264a803620
janxin
2020-06-14 18:01:28 +08:00
@also24 原因在于 API 网关可能最开始没支持这玩意
also24
2020-06-14 18:21:17 +08:00
@janxin #9
那也可以通过对整个 http body 进行 hash,签名放进 header 的方式来处理。

或者将原有 body 整个包装进一个 JSON String 字段来解决,都可以避免字段乱序或序列化方式不统一造成的问题。
shawndev
2020-06-15 09:53:18 +08:00
prettyprinted 设置不同
jifengg
2020-06-16 11:03:27 +08:00
我就喜欢对整个 body 签名的这种,不用管什么参数排序的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/681419

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX