qingwg / payjs

个人支付收款解决方案PayJS的Golang版本SDK
https://payjs.qingwuguo.com
MIT License
106 stars 23 forks source link

签名算法可以改进 #4

Open markity opened 4 years ago

markity commented 4 years ago

写了一个demo, 用于校验payjs响应的签名, 适用于各种复杂的数据类型, 你看着改

package main

import (
    "crypto/md5"
    "encoding/json"
    "fmt"
    "sort"
    "strings"
    "time"
)

var jsonStr = `
{
    "name":"Markity",
    "age":16,
    "child":null,
    "interests":["python","golang"],
    "friends":[
        {"name":"Jack","age":17,"interests":["lua"]},
        {"name":"Mary","age":15,"interests":["java"]}
    ],
    "compary":
    {
        "name":"Microsoft",
        "workders":[
            {"name":"Peter","age":22},
            {"name":"Eric","age":23}
        ]
    },
    "sign":"XXX"
}
`

var mchKey = "xxxxx"

func main() {
    start := time.Now()
    fmt.Println(toolCheckSignResp([]byte(jsonStr), mchKey))

    fmt.Println(time.Since(start))
}

// toolCheckSignResp 校验响应签名
func toolCheckSignResp(respData []byte, mchKey string) bool {
    kvMap := make(map[string]interface{})
    // 因为调用此方法前, 已经将respData Unmarshal为resp结构体, 证明json数据没有错误, 忽略error
    json.Unmarshal(respData, &kvMap)

    // 获取json数据里的签名
    originSign, ok := kvMap["sign"]
    // 对于某些特殊情况, 如重复退款, 即使return_code为1也无sign
    // 所以如果respData中无sign字段, 则返回true
    if !ok {
        return true
    }

    // 获取keys并排序
    delete(kvMap, "sign")
    keys := make([]string, 0)
    for k, _ := range kvMap {
        keys = append(keys, k)
    }
    sort.Strings(keys)

    // 将key=val追加入kvList中
    kvList := make([]string, 0)
    var walkMap func(string, interface{}, interface{}) = nil
    walkMap = func(perfix string, obj interface{}, ks interface{}) {
        // obj为[]interface{}或者map[string]interface{}类型, ks为[]string或者[]int类型
        switch keys := ks.(type) {
        case []string:
            // 当ks为[]string类型时, obj为map[string]interface{}类型
            object := obj.(map[string]interface{})
            for _, key := range keys {
                v := object[key]
                // 根据签名算法, 值为null的不计入
                if v == nil {
                    continue
                }
                // 若v为复合类型, 进入继续遍历
                switch value := v.(type) {
                case []interface{}:
                    _perfix := ""
                    if perfix == "" {
                        _perfix = key
                    } else {
                        _perfix = fmt.Sprintf("%v[%v]", perfix, key)
                    }
                    _keys := make([]int, 0, len(value))
                    for i := 0; i < len(value); i++ {
                        _keys = append(_keys, i)
                    }
                    walkMap(_perfix, value, _keys)
                    continue
                case map[string]interface{}:
                    _perfix := ""
                    if perfix == "" {
                        _perfix = key
                    } else {
                        _perfix = fmt.Sprintf("%v[%v]", perfix, key)
                    }
                    _keys := make([]string, 0)
                    for k, _ := range value {
                        _keys = append(_keys, k)
                    }
                    sort.Strings(_keys)
                    walkMap(_perfix, value, _keys)
                    continue
                }
                // 为基础类型
                if perfix == "" {
                    kvList = append(kvList, fmt.Sprintf("%v=%v", key, v))
                } else {
                    kvList = append(kvList, fmt.Sprintf("%v[%v]=%v", perfix, key, v))
                }
            }
        case []int:
            // keys为[]int类型, perfix不可能为""
            // 当ks为[]int, obj为[]interface{}
            object := obj.([]interface{})
            for _, key := range keys {
                v := object[key]
                if v == nil {
                    continue
                }
                switch value := v.(type) {
                // 若v为复合类型, 进入继续遍历
                case []interface{}:
                    _perfix := fmt.Sprintf("%v[%v]", perfix, key)
                    _keys := make([]int, 0, len(value))
                    for i := 0; i < len(value); i++ {
                        _keys = append(_keys, i)
                    }
                    walkMap(_perfix, value, _keys)
                    continue
                case map[string]interface{}:
                    _perfix := fmt.Sprintf("%v[%v]", perfix, key)
                    _keys := make([]string, 0)
                    for k, _ := range value {
                        _keys = append(_keys, k)
                    }
                    sort.Strings(_keys)
                    walkMap(_perfix, value, _keys)
                    continue
                }
                // 为基础类型
                kvList = append(kvList, fmt.Sprintf("%v[%v]=%v", perfix, key, v))
            }
        }
    }
    walkMap("", kvMap, keys)
    kvList = append(kvList, fmt.Sprintf("key=%v", mchKey))
    kvChain := strings.Join(kvList, "&")
    fmt.Println(kvChain)

    // 比较签名
    realSign := strings.ToUpper(fmt.Sprintf("%x", md5.Sum([]byte(strings.Join(kvList, "&")))))
    if originSign != realSign {
        return false
    }
    return true
}

/*输出
age=16&compary[name]=Microsoft&compary[workders][0][age]=22&compary[workders][0][name]=Peter&compary[workders][1][age]=23&compary[workders][1][name]=Eric&friends[0][age]=17&friends[0][interests][0]=lua&friends[0][name]=Jack&friends[1][age]=15&friends[1][interests][0]=java&friends[1][name]=Mary&interests[0]=python&interests[1]=golang&name=Markity&key=xxxxx
false
132.6µs
*/