V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Nick66
V2EX  ›  程序员

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

  •  
  •   Nick66 · 2020-06-14 13:21:54 +08:00 · 2608 次点击
    这是一个创建于 1624 天前的主题,其中的信息可能已经有所发展或是发生改变。

    源码如下

    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
    
    }
    
    第 1 条附言  ·  2020-06-14 14:01:12 +08:00
    找到解决方法了,转成 json 之后去掉 `\n` 就可以

    ```
    h.Write([]byte(strings.Trim(data, "\n")))
    ```
    12 条回复    2020-06-16 11:03:27 +08:00
    also24
        1
    also24  
       2020-06-14 13:27:38 +08:00 via Android
    说个题外话:
    也不知道谁带起的这种风气,用这种方式来进行检验。

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

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

    这样就不涉及排序问题
    Nick66
        7
    Nick66  
    OP
       2020-06-14 14:01:37 +08:00
    @haiyang416 感谢,解决了。
    Vegetable
        8
    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
        9
    janxin  
       2020-06-14 18:01:28 +08:00
    @also24 原因在于 API 网关可能最开始没支持这玩意
    also24
        10
    also24  
       2020-06-14 18:21:17 +08:00
    @janxin #9
    那也可以通过对整个 http body 进行 hash,签名放进 header 的方式来处理。

    或者将原有 body 整个包装进一个 JSON String 字段来解决,都可以避免字段乱序或序列化方式不统一造成的问题。
    shawndev
        11
    shawndev  
       2020-06-15 09:53:18 +08:00
    prettyprinted 设置不同
    jifengg
        12
    jifengg  
       2020-06-16 11:03:27 +08:00
    我就喜欢对整个 body 签名的这种,不用管什么参数排序的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2812 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 07:32 · PVG 15:32 · LAX 23:32 · JFK 02:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.