Golang實現結構體簽名驗籤&grpc/gin中間件

設計目標

  • 支持RSA2加簽驗籤(解析密鑰方式:PKCS1 數字簽名算法:SHA256)
  • 支持grpc攔截器加簽驗籤,對業務代碼無侵入
  • 支持gin框架中間件驗籤,支持客戶端發送http請求設置加簽信息到Header中
  • 支持服務端對接多語言客戶端(簽名原文爲:有序JSON(ASCII碼序排序Key,忽略結構體/Map中的0值和空值),RSA2加簽(PKCS1+SHA256))

簽名

簽名接口

加簽接口
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"
	"sort"
	"strings"
)

type SignType string

const (
	RSA2 SignType = "SHA256WithRSA"
)

const InvalidType = "invalid type=%v"

var ErrPemDecode = errors.New("pem.Decode failed")

func NewRSASigner() *Signer {
	return &Signer{
		Type: RSA2,
	}
}

type Signer struct {
	Type SignType
}

func (s *Signer) Sign(content, privateKey string) (sign string, err error) {
	if s.Type == RSA2 {
		return rsa2Sign(content, privateKey)
	}
	return
}

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
}

func NewRSAVerifier() *Verifier {
	return &Verifier{
		Type: RSA2,
	}
}

type Verifier struct {
	Type SignType
}

func (s *Verifier) Verify(content, sign, pubKey string) (err error) {
	if s.Type == RSA2 {
		return rsa2Verify(content, sign, pubKey)
	}
	return
}

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
}

type ToSignMap map[string]interface{}

func (sc ToSignMap) ToSortedNoZeroValue() ToSignMap {
	if len(sc) == 0 {
		return sc
	}

	// 1、取出sc的值不爲空的key
	var keys []string
	for k, v := range sc {
		// 忽略空值
		if k == "" || v == "" {
			continue
		}
		keys = append(keys, k)
	}

	// 2、排序
	sort.Strings(keys)

	// 3、重組爲排序的map
	sorted := make(ToSignMap)
	for _, v := range keys {
		sorted[v] = sc[v]
	}
	return sorted
}

func (sc ToSignMap) ToSortedNoZeroValueJSON() (content string, err error) {
	sorted := sc.ToSortedNoZeroValue()
	if len(sorted) == 0 {
		return
	}

	// 轉換爲Json
	js, err := json.Marshal(sorted)
	if err != nil {
		return
	}
	content = string(js)
	return
}

// 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
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章