-
Notifications
You must be signed in to change notification settings - Fork 0
/
sign.go
120 lines (95 loc) · 3.13 KB
/
sign.go
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 sign
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
)
// Sign will sign the request with appKey and appKeySecret.
func Sign(req *http.Request, appKey, appKeySecret string) error {
req.Header.Set(HTTPHeaderCAKey, appKey)
req.Header.Set(HTTPHeaderCANonce, UUID4())
req.Header.Set(HTTPHeaderCASignatureMethod, defaultSignMethod)
req.Header.Set(HTTPHeaderCATimestamp, CurrentTimeMillis())
if req.Header.Get(HTTPHeaderAccept) == "" {
req.Header.Set(HTTPHeaderAccept, defaultAccept)
}
if req.Header.Get(HTTPHeaderDate) == "" {
req.Header.Set(HTTPHeaderDate, CurrentGMTDate())
}
if req.Header.Get(HTTPHeaderUserAgent) == "" {
req.Header.Set(HTTPHeaderUserAgent, defaultUserAgent)
}
ct := req.Header.Get(HTTPHeaderContentType)
if req.Body != nil && ct != HTTPContentTypeForm &&
!strings.HasPrefix(ct, HTTPContentTypeMultipartFormWithBoundary) {
b, err := ioutil.ReadAll(req.Body)
if err != nil {
return fmt.Errorf("read all request body err: %w", err)
}
if err := req.Body.Close(); err != nil {
return fmt.Errorf("request body close err: %w", err)
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(b))
req.Header.Set(HTTPHeaderContentMD5, MD5(b))
}
stringToSign, err := buildStringToSign(req)
if err != nil {
return fmt.Errorf("build string to sign err: %w", err)
}
req.Header.Set(HTTPHeaderCASignature, HmacSHA256([]byte(stringToSign), []byte(appKeySecret)))
return nil
}
func buildStringToSign(req *http.Request) (string, error) {
var s strings.Builder
s.WriteString(strings.ToUpper(req.Method) + defaultLF)
s.WriteString(req.Header.Get(HTTPHeaderAccept) + defaultLF)
s.WriteString(req.Header.Get(HTTPHeaderContentMD5) + defaultLF)
s.WriteString(req.Header.Get(HTTPHeaderContentType) + defaultLF)
s.WriteString(req.Header.Get(HTTPHeaderDate) + defaultLF)
s.WriteString(buildHeaderStringToSign(req))
paramStr, err := buildParamStringToSign(req)
if err != nil {
return "", fmt.Errorf("build param string to sign err: %w", err)
}
s.WriteString(paramStr)
return s.String(), nil
}
func buildHeaderStringToSign(req *http.Request) string {
var builder strings.Builder
signHeaderKeys := make([]string, 0)
headerKeys := getSortKeys(req.Header, true)
for _, key := range headerKeys {
if _, ok := signHeaders[key]; ok {
signHeaderKeys = append(signHeaderKeys, key)
builder.WriteString(key + ":" + req.Header.Get(key) + defaultLF)
}
}
req.Header.Set(HTTPHeaderCASignatureHeaders, strings.Join(signHeaderKeys, defaultSep))
return builder.String()
}
func buildParamStringToSign(req *http.Request) (string, error) {
clone, err := copyRequest(req)
if err != nil {
return "", fmt.Errorf("copy request err: %w", err)
}
if err := clone.ParseForm(); err != nil {
return "", fmt.Errorf("parse form err: %w", err)
}
paramKeys := getSortKeys(clone.Form)
paramList := make([]string, 0)
for _, key := range paramKeys {
value := clone.Form.Get(key)
if value == "" {
paramList = append(paramList, key)
} else {
paramList = append(paramList, key+"="+value)
}
}
params := strings.Join(paramList, "&")
if params != "" {
params = "?" + params
}
return clone.URL.Path + params, nil
}