【Golang 接口自动化06】微信支付md5签名计算及其优化

前言

可能看过我博客的朋友知道我主要是做的支付这一块的测试工作。而我们都知道现在比较流行的支付方式就是微信支付和支付宝支付,当然最近在使用低手续费大力推广的京东金融(已改名为京东数科)以后也可能站到第一队列,但是要在中国市场走到和财付通、蚂蚁金服一个层级就任重而道远了。

废话不多说,我们一起来看看微信支付签名的官方文档。搜索微信支付--点击支付开发文档--接口规则--安全规范。
1172048-20180815223812753-1043415792.png

我们会看的以下的内容:
1172048-20180815223456419-842409036.png

签名生成的方法文档已经说的很清晰,下面我们一起来看看怎么使用golang来实现它,以及怎么使用一些更高级的特性来优化。

初始方式

最开始的方式比较直接,能实现这个需求就行:

func GetSign(sourceMap map[string]string, bizKey string) string {
    orderedString := orderParam(sourceMap, bizKey)
    md5Ctx := md5.New()
    md5Ctx.Write([]byte(orderedString))
    signString := md5Ctx.Sum(nil)
    //fmt.Print(hex.EncodeToString(cipherStr))
    return hex.EncodeToString(signString)
}

func orderParam(source map[string]string, bizKey string) string {
    var tempArr []string
    i := 0
    for k, v := range source {
        tempArr = append(tempArr, k+"="+v)
        i++
    }
    sort.Strings(tempArr)
    temString := ""
    for n, v := range tempArr {
        if n+1 < len(tempArr) {
            temString = temString + v + "&"
        } else {
            temString = temString + v + bizKey
        }
    }
    fmt.Println(temString)
    return temString
}
代码说明
  • orderParam主要用来把传递的参数转化为键值对的格式(即key1=value1&key2=value2…)并在最后拼接上key
  • GetSign 获取orderParam拼接之后字符串进行md5加密

后来发现这样的方式有很多的弊端,比如无法处理可能某个参数是数字的情况,无法处理某个参数的value值是map或数组的情况,所以就进行了兼容性和性能上的优化。

优化

这一次的优化主要就是添加了格式的兼容,将传入的参数变成了可以存储任何类型数据的interface{},另外就是优化了拼接字符串的操作。优化后的代码如下

func betterOne(srcmap map[string]interface{}, bizkey string) string {
    md5ctx := md5.New()
    keys := make([]string, 0, len(srcmap))

    for k := range srcmap {
        if k == "sign" {
            continue
        }
        keys = append(keys, k)
    }
    sort.Strings(keys)
    var buf bytes.Buffer
    for _, k := range keys {
        vs := srcmap[k]
        if vs == "" {
            continue
        }
        if buf.Len() > 0 {
            buf.WriteByte('&')
        }

        buf.WriteString(k)
        buf.WriteByte('=')
        switch vv := vs.(type) {
        case string:
            buf.WriteString(vv)
        case int:
            buf.WriteString(strconv.FormatInt(int64(vv), 10))
        default:
            panic("params type not supported")
        }
    }
    buf.WriteString(bizkey)
    md5ctx.Write([]byte(buf.String()))
    return hex.EncodeToString(md5ctx.Sum(nil))
}

buf.WriteString使用buffer来替代循环的字符串操作,来自于基础库http库中的url.encode方法,在此前【Golang 接口自动化01】使用标准库net/http发送Get请求提到过这个方法。理论上来说性能会有一个比较大的提升,实际测试结果如下:
1172048-20181012151740139-1586739465.png

其中ns/opB/opallocs/op分别代表每个操作的耗时、分配内存、分配对象次数。可以看的三者都有较大的提升。

当时在知道这个差距之后,我放下了手上的已经构建得七七八八的自动化代码,专心的研究了一段时间开源项目的源码,所以有了下面这个兼容性更好和性能更平衡的版本。

最终方法

这个版本主要考虑的是不同数据兼容性,兼容了直接传递struct时进行签名的计算(后面会学习到直接把struct当作json发送的方法),并把map[string]stringmap[string]interface{}的数据都进行了对应处理。

性能对比

下面是三者对比的性能测试结果
1172048-20181012170241389-1734574122.png

可以看到最终版比优化版损耗的时间还要略短。

参考代码
// Getsign get the sign info
func Getsign(srcdata interface{}, bizkey string) string {
    md5ctx := md5.New()

    switch v := reflect.ValueOf(srcdata); v.Kind() {
    case reflect.String:
        md5ctx.Write([]byte(v.String() + bizkey))
        return hex.EncodeToString(md5ctx.Sum(nil))
    case reflect.Map:
        orderStr := orderParam(v.Interface(), bizkey)
        md5ctx.Write([]byte(orderStr))
        return hex.EncodeToString(md5ctx.Sum(nil))
    case reflect.Struct:
        orderStr := Struct2map(v.Interface(), bizkey)
        md5ctx.Write([]byte(orderStr))
        return hex.EncodeToString(md5ctx.Sum(nil))
    default:
        return ""
    }
}

func orderParam(source interface{}, bizKey string) (returnStr string) {
    switch v := source.(type) {
    case map[string]string:
        keys := make([]string, 0, len(v))

        for k := range v {
            if k == "sign" {
                continue
            }
            keys = append(keys, k)
        }
        sort.Strings(keys)
        var buf bytes.Buffer
        for _, k := range keys {
            if v[k] == "" {
                continue
            }
            if buf.Len() > 0 {
                buf.WriteByte('&')
            }

            buf.WriteString(k)
            buf.WriteByte('=')
            buf.WriteString(v[k])
        }
        buf.WriteString(bizKey)
        returnStr = buf.String()
    case map[string]interface{}:
        keys := make([]string, 0, len(v))

        for k := range v {
            if k == "sign" {
                continue
            }
            keys = append(keys, k)
        }
        sort.Strings(keys)
        var buf bytes.Buffer
        for _, k := range keys {
            if v[k] == "" {
                continue
            }
            if buf.Len() > 0 {
                buf.WriteByte('&')
            }

            buf.WriteString(k)
            buf.WriteByte('=')
            // buf.WriteString(srcmap[k])
            switch vv := v[k].(type) {
            case string:
                buf.WriteString(vv)
            case int:
                buf.WriteString(strconv.FormatInt(int64(vv), 10))
            default:
                panic("params type not supported")
            }
        }
        buf.WriteString(bizKey)
        returnStr = buf.String()
    }
    // fmt.Println(returnStr)
    return
}

func Struct2map(content interface{}, bizKey string) string {
    var tempArr []string
    temString := ""
    var val map[string]string
    if marshalContent, err := json.Marshal(content); err != nil {
        fmt.Println(err)
    } else {
        d := json.NewDecoder(bytes.NewBuffer(marshalContent))
        d.UseNumber()
        if err := d.Decode(&val); err != nil {
            fmt.Println(err)
        } else {
            for k, v := range val {
                val[k] = v
            }
        }
    }
    i := 0
    for k, v := range val {
        // 去除冗余未赋值struct
        if v == "" {
            continue
        }
        i++
        tempArr = append(tempArr, k+"="+v)
    }
    sort.Strings(tempArr)
    for n, v := range tempArr {
        if n+1 < len(tempArr) {
            temString = temString + v + "&"
        } else {
            temString = temString + v + bizKey
        }
    }
    return temString
}

总结

  • 微信签名
  • buffer
  • 简单性能测试

转载于:https://www.cnblogs.com/Detector/p/9741189.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值