设计目标

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

签名

签名接口

1
2
3
4
5
6
7
8
9
10
加签接口
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)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
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
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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端的应用

公共方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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
}