Golang实现结构体签名验签&grpc/gin中间件

设计目标

  • 支持RSA2加签验签(解析密钥方式:PKCS1 数字签名算法:SHA256)
  • 支持grpc拦截器加签验签,对业务代码无侵入
  • 支持gin框架中间件验签,支持客户端发送http请求设置加签信息到Header中
  • 支持服务端对接多语言客户端(签名原文为:有序JSON(ASCII码序排序Key,忽略结构体/Map中的0值和空值),RSA2加签(PKCS1+SHA256))

不足

  • HTTP请求时,application/json类型POST请求签名原文区分类型,其他类型框架无法获取值的类型,只能转化为map[string]string后进行签名
  • 为了通用性,没有在通用参数中加入timestamp, nonce等参数,不能防重放攻击

签名

签名接口

加签接口
func Sign(content, privateKey string)(sign string, err error)
 
验签接口
func Verify(content, sign, pubKey string) (err error)
 
结构体、Map等转换为JSON字符串接口
// InterfaceToSortedJSONStr 结构体、Map 转 待加签的排序的json字符串
// json按照字典序排序,值为空或者为0的忽略,不序列化为json的忽略(tag中`json:"-"`),不参与加签的字段忽略(tag中`sign:"-"`)
func InterfaceToSortedJSONStr(i interface{}) (str string, err error)

代码

package signature

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"reflect"
	"strings"
)

// Sign 加签函数
type Sign func(content, privateKey string) (sign string, err error)

// Verify 验签函数
type Verify func(content, sign, pubKey string) (err error)

// NewSigner 初始化Signer,默认RSA2签名
func NewSigner(s Sign) *Signer {
	if s == nil {
		s = rsa2Sign
	}
	return &Signer{
		S: s,
	}
}

var ErrPemDecode = errors.New("pem.Decode failed") // pem解析失败

func rsa2Sign(content, privateKey string) (sign string, err error) {
	// 1、将密钥解析成密钥实例
	block, _ := pem.Decode([]byte(privateKey))
	if block == nil {
		err = ErrPemDecode
		return
	}
	key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return
	}

	// 2、生成签名
	hash := sha256.New()
	_, err = hash.Write([]byte(content))
	if err != nil {
		return
	}
	signature, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, hash.Sum(nil))
	if err != nil {
		return
	}

	// 3、签名base64编码
	sign = base64.StdEncoding.EncodeToString(signature)
	return
}

// Signer
type Signer struct {
	S Sign
}

// Sign 签名
func (s *Signer) Sign(content, privateKey string) (sign string, err error) {
	return s.S(content, privateKey)
}

// NewVerifier 初始化Verifier,默认RSA2验签
func NewVerifier(v Verify) *Verifier {
	if v == nil {
		v = rsa2Verify
	}
	return &Verifier{
		V: v,
	}
}

func rsa2Verify(content, sign, pubKey string) (err error) {
	// 1、签名base64解码
	signature, err := base64.StdEncoding.DecodeString(sign)
	if err != nil {
		return
	}

	// 2、密钥解析成公钥实例
	block, _ := pem.Decode([]byte(pubKey))
	if block == nil {
		err = ErrPemDecode
		return
	}
	key, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return
	}
	hash := sha256.New()
	_, err = hash.Write([]byte(content))
	if err != nil {
		return
	}

	// 3、验证签名
	pub := key.(*rsa.PublicKey)
	err = rsa.VerifyPKCS1v15(pub, crypto.SHA256, hash.Sum(nil), signature)
	return
}

// Verifier
type Verifier struct {
	V Verify
}

// Verify 验签
func (s *Verifier) Verify(content, sign, pubKey string) (err error) {
	return s.V(content, sign, pubKey)
}

const InvalidType = "invalid type=%v"

// InterfaceToSortedJSONStr 结构体、Map 转 待加签的排序的json字符串
// json按照字典序排序,值为空或者为0的忽略,不序列化为json的忽略(tag中`json:"-"`),不参与加签的字段忽略(tag中`sign:"-"`)
func InterfaceToSortedJSONStr(i interface{}) (str string, err error) {
	// 1、数据提取,基础类型提取值,结构体、Map等转换为有序Map
	if i == nil {
		err = fmt.Errorf(InvalidType, i)
		return
	}
	v, err := interfaceValExtract(i)
	if err != nil {
		return
	}

	// 2、字符串类型直接返回
	if vStr, ok := v.(string); ok {
		str = vStr
		return
	}

	// 3、序列化为json
	js, err := json.Marshal(v)
	if err != nil {
		return
	}
	str = string(js)
	return
}

// interfaceValExtract 提取i的值,i为0值或空值时返回"",结构体、Map 转 key排序的Map[string]interface{}
func interfaceValExtract(i interface{}) (v interface{}, err error) {
	// 1、构建默认返回值,反射获取i的类型与值
	v = ""
	typ := reflect.TypeOf(i)
	val := reflect.ValueOf(i)

	// 2、指针类型取出元素类型与值
	if typ.Kind() == reflect.Ptr {
		if val.IsNil() {
			return
		}
		typ = typ.Elem()
		val = val.Elem()
	}

	// 3、分类型处理
	k := typ.Kind()
	switch k {
	case reflect.Bool:
		v = val.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		// 忽略0值
		if val.Int() == 0 {
			return
		}
		v = val.Int()
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		// 忽略0值
		if val.Uint() == 0 {
			return
		}
		v = val.Uint()
	case reflect.Float32, reflect.Float64:
		if val.IsZero() {
			return
		}
		v = val.Float()
	case reflect.String:
		v = val.String()
	case reflect.Slice, reflect.Array:
		if val.Len() == 0 {
			return
		}
		v, err = sliceValExtract(val)
	case reflect.Struct:
		if val.IsZero() {
			return
		}
		v, err = structValToSortedMap(typ, val)
	case reflect.Map:
		if val.Len() == 0 {
			return
		}
		v, err = mapValToSortedMap(val)
	// 其他类型不支持签名
	default:
		err = fmt.Errorf(InvalidType, k)
	}
	return
}

// structValToSortedMap 结构体转排序的json string,忽略空值和0值
func structValToSortedMap(typs reflect.Type, vals reflect.Value) (sc ToSignMap, err error) {
	// 1、构建map
	sc = make(ToSignMap)

	// 2、反射遍历属性
	num := vals.NumField()
	for i := 0; i < num; i++ {
		val := vals.Field(i)
		typ := typs.Field(i)
		// 判断是否为需要忽略的加签字段
		if isSkippedSignField(typ.Tag) {
			continue
		}
		// 判断属性是否可导出(私有属性不能导出)
		if !val.CanInterface() {
			continue
		}
		// 转换成排序类型
		var v interface{}
		v, err = interfaceValExtract(val.Interface())
		if err != nil {
			return
		}
		// 名称以结构体中的json标签名称为准
		name := typ.Name
		if jsonName := getJSONNameInTag(typ.Tag); jsonName != "" {
			name = jsonName
		}
		sc[name] = v
	}

	// 3、元素排序、去掉空值
	sc = sc.ToSortedNoZeroValue()
	return
}

func isSkippedSignField(tag reflect.StructTag) bool {
	// 1、忽略不序列化的字段
	v, ok := tag.Lookup("json")
	if ok && v == "-" {
		return true
	}

	// 2、忽略不加签的字段
	v, ok = tag.Lookup("sign")
	return ok && v == "-"
}

func getJSONNameInTag(tag reflect.StructTag) string {
	v, ok := tag.Lookup("json")
	if ok {
		return strings.Split(v, ",")[0]
	}
	return ""
}

// mapValToSortedMap map转排序的json string,忽略0值和空值
func mapValToSortedMap(vals reflect.Value) (sc ToSignMap, err error) {
	// 1、构建map
	sc = make(ToSignMap)
	// 2、反射遍历属性
	iter := vals.MapRange()
	for iter.Next() {
		// 处理key
		key, er := interfaceValExtract(iter.Key().Interface())
		if er != nil {
			err = er
			return
		}
		k := fmt.Sprintf("%v", key)

		// 处理value
		var val interface{}
		val, err = interfaceValExtract(iter.Value().Interface())
		if err != nil {
			return
		}

		// 赋值
		sc[k] = val
	}

	// 3、元素排序、去掉空值
	sc = sc.ToSortedNoZeroValue()
	return
}

// sliceValExtract 切片转忽略空值 或 配置了忽略签名 的切片
func sliceValExtract(vals reflect.Value) (s []interface{}, err error) {
	// 1、反射遍历属性
	num := vals.Len()
	for i := 0; i < num; i++ {
		// 类型判断
		val := vals.Index(i)
		k := val.Kind()
		if isNotValidType(k) {
			err = fmt.Errorf(InvalidType, k)
			return
		}

		// 判断属性是否可导出(私有属性不能导出)
		if !val.CanInterface() {
			continue
		}
		// 取出值
		v := val.Interface()

		// 结构体/Map/切片类型进行值的提取
		if k == reflect.Struct || k == reflect.Map || k == reflect.Slice || k == reflect.Array {
			// 提取切片的元素
			v, err = interfaceValExtract(val.Interface())
			if err != nil {
				return
			}
		}
		s = append(s, v)
	}

	// 2、返回处理后的切片
	return
}

func isNotValidType(k reflect.Kind) bool {
	return k == reflect.Invalid || k == reflect.Complex64 || k == reflect.Complex128 ||
		k == reflect.Chan || k == reflect.Func || k == reflect.UnsafePointer
}

package signature

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

type ProtoTest struct {
	ID                   int                       `protobuf:"varint,1,opt,name=offset,proto3" json:"id" sign:"-"`
	Flag                 bool                      `protobuf:"varint,1,opt,name=offset,proto3" json:"flag"`
	Dou                  float32                   `protobuf:"varint,1,opt,name=offset,proto3" json:"dou"`
	Str                  string                    `protobuf:"varint,1,opt,name=offset,proto3" json:"str"`
	Val1                 map[int]string            `protobuf:"varint,1,opt,name=offset,proto3" json:"val1"`
	Val2                 map[string]string         `protobuf:"varint,1,opt,name=offset,proto3" json:"val2"`
	Val3                 []map[string]InnerTest1   `protobuf:"varint,1,opt,name=offset,proto3" json:"val3"`
	Val4                 [][]map[string]InnerTest1 `protobuf:"varint,1,opt,name=offset,proto3" json:"val4"`
	Arr                  []InnerTest1              `protobuf:"varint,1,opt,name=offset,proto3" json:"arr"`
	Arr1                 []int                     `protobuf:"varint,1,opt,name=offset,proto3" json:"arr1"`
	Inner                InnerTest1                `protobuf:"varint,1,opt,name=offset,proto3" json:"inner"`
	Inner1               InnerTest1                `protobuf:"varint,1,opt,name=offset,proto3" json:"inner1"`
	Flags                []bool                    `protobuf:"varint,1,opt,name=offset,proto3" json:"flags"`
	XXX_NoUnkeyedLiteral struct{}                  `json:"-"`
	XXX_unrecognized     []byte                    `json:"-"`
	XXX_sizecache        int32                     `json:"-"`
}

type InnerTest1 struct {
	Val   map[string]string `protobuf:"varint,1,opt,name=offset,proto3" json:"val" sign:"-"`
	Inner *InnerTest2       `protobuf:"varint,1,opt,name=offset,proto3" json:"inner"`
}

type InnerTest2 struct {
	ID int `protobuf:"varint,1,opt,name=offset,proto3" json:"id"`
}

var (
	innerTest1 = InnerTest1{
		Val: map[string]string{
			"a": "a",
			"b": "b",
		},
		Inner: &InnerTest2{
			ID: 1,
		},
	}
	pt = &ProtoTest{
		ID: 1,
		Val1: map[int]string{
			1: "1",
			2: "2",
		},
		Val2: map[string]string{
			"a": "a",
			"b": "b",
		},
		Val3:  []map[string]InnerTest1{{"val3": innerTest1}},
		Val4:  [][]map[string]InnerTest1{{{"val4": innerTest1}}},
		Arr:   []InnerTest1{innerTest1},
		Arr1:  []int{1, 0, 3, 2, 4},
		Inner: innerTest1,
		Flags: []bool{true, false},
	}
	mt = map[string]interface{}{
		"id":       1,
		"dou":      3.14,
		"pt":       pt,
		"str":      "str",
		"strEmpty": "",
		"":         1,
	}
)

const jsonStr = `{"dou":3.14,"id":1,"pt":{"arr":[{"inner":{"id":1}}],"flag":false,"flags":[true,false],"inner":{"inner":{"id":1}},"val1":{"1":"1","2":"2"},"val2":{"a":"a","b":"b"}},"str":"str"}`

func TestInterfaceToSortedJsonStr(t *testing.T) {
	testAssert := assert.New(t)
	tests := []struct {
		origin interface{}
		sign   string
	}{
		{pt, `{"arr":[{"inner":{"id":1}}],"arr1":[1,0,3,2,4],"flag":false,"flags":[true,false],"inner":{"inner":{"id":1}},"val1":{"1":"1","2":"2"},"val2":{"a":"a","b":"b"},"val3":[{"val3":{"inner":{"id":1}}}],"val4":[[{"val4":{"inner":{"id":1}}}]]}`},
		{mt, `{"dou":3.14,"id":1,"pt":{"arr":[{"inner":{"id":1}}],"arr1":[1,0,3,2,4],"flag":false,"flags":[true,false],"inner":{"inner":{"id":1}},"val1":{"1":"1","2":"2"},"val2":{"a":"a","b":"b"},"val3":[{"val3":{"inner":{"id":1}}}],"val4":[[{"val4":{"inner":{"id":1}}}]]},"str":"str"}`},
		{jsonStr, jsonStr},
		{1, "1"},
		{false, "false"},
		{"", ""},
	}
	for _, test := range tests {
		sign, err := InterfaceToSortedJSONStr(test.origin)
		testAssert.Equal(sign, test.sign)
		testAssert.Equal(err, nil)
	}
}

const (
	rsaPrivateKey = `
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCY4/TH2UpkW5pRgdmvkwGQGWFt1E2a76j9s1gmm0wOiByLQ1KQ
NuJ1c3SBpAKcIMh4841cf3t1HPTttgaK/51RGq7AN+R7naKnFWg20WGzkEpHzS4E
JM+S1bOtyz260ZhunxMA4HmmWPDq94lczfMEss/wjKL+r9R3HIeh21cKfwIDAQAB
AoGAEanYaFRay2Bn4j3JvAaUWiUMhAdQlfNVR0Y2i3NKpK0l+xLikYW9wQr/LVEY
+hexgYPF06doyH15cJMki19/uaawZLVRTv8tiTD+XHlpjFUpVlf52/be19gK+/ZL
mqjs2WQggJMyzH/OvBnvkqxEpqf5ilIUAvJWgJ6wfYUBHhUCQQC3u2Map9scywhQ
dzP4u0INvFKKrgz2O64uwf7Gn5rbXRsDTl8tLUXoiGiOGNjNtX/y4CeLjRn5ezs+
ZDm4EHddAkEA1QcHPnjzusJogGvy8iSVfqTDbby+KzhTYxMFaaA0q4r91Kz1BVP+
kc47n24G3y3Zhs5rro78loRpdJOeUfJ3iwJAFbxEUB31bOWT+Tjw3AcDHG7f8OoA
PIz44S0v/71X64WLMYvu9IA7mfOxMsY7t7I2Dbx40SiDHyF1876VmXHRPQJAVSI0
6+uMhBOTjdcWRV0HfZA9JcrrOPyOnqaIYDkNM40defQRC6sQrpZ7z3A6QNDjAPPX
pvAv07thJZylBdzflwJABTzHbnZ+R6av1Qz8zsicHAC6YG1PuprXO40X/Icl8W+D
yNwv2bKrpA9MxS2bFcC9wtVeeWgE1oyJBJD8pEQonQ==
-----END RSA PRIVATE KEY-----
`
	rsaPubKey = `
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCY4/TH2UpkW5pRgdmvkwGQGWFt
1E2a76j9s1gmm0wOiByLQ1KQNuJ1c3SBpAKcIMh4841cf3t1HPTttgaK/51RGq7A
N+R7naKnFWg20WGzkEpHzS4EJM+S1bOtyz260ZhunxMA4HmmWPDq94lczfMEss/w
jKL+r9R3HIeh21cKfwIDAQAB
-----END PUBLIC KEY-----
`
)

func TestSign(t *testing.T) {
	testAssert := assert.New(t)
	sign, err := sign()
	testAssert.Equal(sign, "lQuCpp3kW8udrTNtaKcGTPDeGelxIHEXqp4u3n1owDlFRQtbqKpPoLxICHt5ahEf4WvWiuoAqofJqv52/PhjPPKDWawMVZJlgP38bxkvD6Y1+pgXSvKSm+LXHpHQRExcLiHUvytWJ6U+C0geDoswdGMeHiRxT9IX6nWovKayZrk=")
	testAssert.Equal(err, nil)
}

func sign() (string, error) {
	str, _ := InterfaceToSortedJSONStr(mt)
	return NewRSASigner().Sign(str, rsaPrivateKey)
}

func TestVerify(t *testing.T) {
	testAssert := assert.New(t)
	str, _ := InterfaceToSortedJSONStr(pt)
	sign, _ := sign()
	err := NewRSAVerifier().Verify(str, sign, rsaPubKey)
	testAssert.Equal(err, nil)
}

中间件

名词解释

App:访问server端的应用

公共方法

package signmiddleware

import "xxx/signature"

const (
	SignAppIDKey   = "appID" // app ID key, http请求时设置appID到Header中, grpc请求时client拦截器自动完成 设置到context中
	SignValueKey   = "sign"  // 签名 key, http请求时设置sign到Header中, grpc请求时client拦截器自动完成 设置到context中
	ErrAppIDorSign = "app id or sign is not valid, app id=%v"
)

type SignClient struct {
	AppID      string // app ID
	PrivateKey string // 私钥
}

type GetPublicKeysByID func(appID string) ([]string, error)

// CreateSign 生成签名
func CreateSign(request interface{}, privateKey string) (sign string, err error) {
	// 1、req转有序json
	toSignJSON, err := signature.InterfaceToSortedJSONStr(request)
	if err != nil {
		return
	}

	// 2、签名
	sign, err = signature.NewRSASigner().Sign(toSignJSON, privateKey)

	return
}

// VerifySign 验证签名
func VerifySign(request interface{}, sign string, pubKeys []string) (err error) {
	// 1、req转有序json
	toSignJSON, err := signature.InterfaceToSortedJSONStr(request)
	if err != nil {
		return
	}

	// 2、支持多个公钥验签,密钥升级时,兼容旧的请求
	verifier := signature.NewRSAVerifier()
	for _, v := range pubKeys {
		err = verifier.Verify(toSignJSON, sign, v)
		// 验签成功,跳出循环
		if err == nil {
			break
		}
	}

	return
}

GRPC中间件

package grpcsign

import (
	"context"
	"fmt"

	"xxx/signmiddleware"
	"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
	"google.golang.org/grpc"
)

func SignUnaryServerInterceptor(getPubKey signmiddleware.GetPublicKeysByID) grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
		// 1、获取context中自定义的属性
		appID := GetAppIDFromCtx(ctx)
		sign := getSignFromCtx(ctx)
		if appID == "" || sign == "" {
			return nil, fmt.Errorf(signmiddleware.ErrAppIDorSign, appID)
		}

		// 2、根据AppID获取公钥
		pubKeys, err := getPubKey(appID)
		if err != nil {
			return nil, err
		}

		// 3、验证签名
		err = signmiddleware.VerifySign(req, sign, pubKeys)
		// 验签失败
		if err != nil {
			return nil, err
		}

		v, err := handler(ctx, req)
		return v, err
	}
}

func GetAppIDFromCtx(ctx context.Context) string {
	return metautils.ExtractIncoming(ctx).Get(signmiddleware.SignAppIDKey)
}

func getSignFromCtx(ctx context.Context) string {
	return metautils.ExtractIncoming(ctx).Get(signmiddleware.SignValueKey)
}

func SignUnaryClientInterceptor(signC *signmiddleware.SignClient) grpc.UnaryClientInterceptor {
	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
		// 1、生成签名
		sign, err := signmiddleware.CreateSign(req, signC.PrivateKey)
		if err != nil {
			return err
		}

		// 3、appID及签名设置到context, grpc自定义key只能使用grpc提供的metadata接口
		newCtx := metautils.ExtractOutgoing(ctx).Clone().Set(signmiddleware.SignAppIDKey, signC.AppID).Set(signmiddleware.SignValueKey, sign).ToOutgoing(ctx)

		// 4、调用服务端
		err = invoker(newCtx, method, req, reply, cc, opts...)
		return err
	}
}

GIN中间件

package ginsign

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/url"

	"xxx/log"
	"xxx/signature"
	"xxx/signmiddleware"

	"github.com/gin-gonic/gin"
)

// SignClientToHeader http请求加签,设置签名内容到header中,客户端使用
func SignClientToHeader(header map[string]string, req interface{}, signC *signmiddleware.SignClient) (newHeader map[string]string, err error) {
	// 1、生成sign
	sign, err := signmiddleware.CreateSign(req, signC.PrivateKey)
	if err != nil {
		return
	}

	// 2、设置到Header中
	if len(header) == 0 {
		header = make(map[string]string)
	}
	header[signmiddleware.SignAppIDKey] = signC.AppID
	header[signmiddleware.SignValueKey] = sign
	newHeader = header
	return
}

// SignServerVerify gin验签中间件
func SignServerVerify(c *gin.Context, getPubKeysByID signmiddleware.GetPublicKeysByID) (err error) {
	// 1、从header中取出签名内容
	appID := c.GetHeader(signmiddleware.SignAppIDKey)
	sign := c.GetHeader(signmiddleware.SignValueKey)
	if appID == "" || sign == "" {
		log.Warningf(c, "client signature is invalid, appID=%v, sign=%v", appID, sign)
		err = fmt.Errorf(signmiddleware.ErrAppIDorSign, appID)
		return
	}

	// 2、根据App ID获取公钥
	pubKeys, err := getPubKeysByID(appID)
	if err != nil {
		log.Warningf(c, "svc.GetAppNameByID failed, appID=%v, err=%v", appID, err)
		return
	}

	// 3、初始化待签内容, request中的参数转content
	var content string
	switch {
	case c.Request.Method == "GET":
		content, err = convertURLValToSignJSON(c.Request.Form)
	case c.ContentType() == "application/json":
		content, err = convertBodyToSignJSON(c)
	default:
		content, err = convertURLValToSignJSON(c.Request.PostForm)
	}
	if err != nil {
		return
	}

	// 4、验证签名
	err = signmiddleware.VerifySign(content, sign, pubKeys)
	// 验签失败
	if err != nil {
		log.Warningf(c, "sign.middleware.VerifySign failed, appID=%v, content=%v, sign=%v, err=%v", appID, content, sign, err)
	}
	return
}

func convertURLValToSignJSON(values url.Values) (content string, err error) {
	if len(values) == 0 {
		return
	}

	// 构建排序map
	sc := make(signature.ToSignMap)
	for k, v := range values {
		if len(v) == 0 {
			continue
		}
		sc[k] = v[0]
	}

	// 转换为JSON
	content, err = sc.ToSortedNoZeroValueJSON()
	return
}

func convertBodyToSignJSON(c *gin.Context) (content string, err error) {
	// 1、获取body []byte
	data, err := c.GetRawData()
	if err != nil {
		return
	}

	// 2、data转map
	content, err = jsonToSorted(data)

	// 3、重新赋值body,以便body可以被再次读取
	c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(data))

	return
}

func jsonToSorted(data []byte) (content string, err error) {
	sc := make(signature.ToSignMap)
	err = json.Unmarshal(data, &sc)
	if err != nil {
		return
	}
	// 转换为有序JSON
	content, err = signature.InterfaceToSortedJSONStr(sc)
	return
}

你可能感兴趣的:(Golang)