-
Notifications
You must be signed in to change notification settings - Fork 9
/
client.go
200 lines (166 loc) · 4.8 KB
/
client.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
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
package wlc
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"io"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"time"
)
type Option func(c *client)
func WithHTTPClient(c *http.Client) Option {
return func(cc *client) {
if c != nil {
cc.client = c
}
}
}
type client struct {
appId string
secretKey string
secretKeyHex []byte
bizId string
client *http.Client
}
// Client 生产客户端
type Client interface {
// Check 实名认证接口,
// 网络游戏用户实名认证服务接口,面向已经接入网络游戏防
// 沉迷实名认证系统的游戏运营单位提供服务,游戏运营单位调用
// 该接口进行用户实名认证工作,本版本仅支持大陆地区的姓名和
// 二代身份证号核实认证。
Check(ctx context.Context, param CheckParam) (*CheckResult, error)
// Query 实名认证结果查询接口,
// 网络游戏用户实名认证结果查询服务接口,面向已经提交用
// 户实名认证且没有返回结果的游戏运营单位提供服务,游戏运营
// 单位可以调用该接口,查询已经提交但未返回结果用户的实名认
// 证结果。
Query(ctx context.Context, ai string) (*QueryResult, error)
// LoginTrace 游戏用户行为数据上报接口
// 游戏用户行为数据上报接口,面向已经接入网络游戏防沉迷
// 实名认证系统的游戏运营单位提供服务,游戏运营单位调用该接
// 口上报游戏用户上下线行为数据。
LoginTrace(ctx context.Context, param LoginTraceParam) ([]*LoginTraceResult, error)
}
// TestClient 接口测试辅助客户端
type TestClient interface {
CheckTest(ctx context.Context, code string, param CheckParam) (*CheckResult, error)
QueryTest(ctx context.Context, code, ai string) (*QueryResult, error)
LoginTraceTest(ctx context.Context, code string, param LoginTraceParam) ([]*LoginTraceResult, error)
}
func New(appId, secretKey, bizId string, opts ...Option) Client {
var nClient = &client{}
nClient.appId = appId
nClient.secretKey = secretKey
nClient.secretKeyHex, _ = hex.DecodeString(secretKey)
nClient.bizId = bizId
nClient.client = http.DefaultClient
for _, opt := range opts {
if opt != nil {
opt(nClient)
}
}
return nClient
}
func NewTest(appId, secretKey, bizId string, opts ...Option) TestClient {
var nClient = &client{}
nClient.appId = appId
nClient.secretKey = secretKey
nClient.secretKeyHex, _ = hex.DecodeString(secretKey)
nClient.bizId = bizId
nClient.client = http.DefaultClient
for _, opt := range opts {
if opt != nil {
opt(nClient)
}
}
return nClient
}
func (c *client) request(ctx context.Context, method, api string, values url.Values, param, result interface{}) error {
if values == nil {
values = url.Values{}
}
var body string
if param != nil {
data, err := json.Marshal(param)
if err != nil {
return err
}
// 加密请求参数
if data, err = c.encrypt(c.secretKeyHex, data); err != nil {
return err
}
// 构造新的请求参数
var payload = struct {
Data string `json:"data"`
}{
Data: base64.StdEncoding.EncodeToString(data),
}
if data, err = json.Marshal(payload); err != nil {
return err
}
body = string(data)
}
var nURL = api
if len(values) > 0 {
nURL = api + "?" + values.Encode()
}
req, err := http.NewRequestWithContext(ctx, method, nURL, strings.NewReader(body))
if err != nil {
return err
}
var now = strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
values.Add("appId", c.appId)
values.Add("bizId", c.bizId)
values.Add("timestamps", now)
var sign = c.sign(c.secretKey, values, body)
req.Header.Set("appId", c.appId)
req.Header.Set("bizId", c.bizId)
req.Header.Set("timestamps", now)
req.Header.Set("sign", sign)
req.Header.Set("Content-Type", "application/json; charset=utf-8")
rsp, err := c.client.Do(req)
if err != nil {
return err
}
defer rsp.Body.Close()
if err = json.NewDecoder(rsp.Body).Decode(result); err != nil {
return err
}
return nil
}
func (c *client) encrypt(secretKeyHex []byte, data []byte) ([]byte, error) {
var block, err = aes.NewCipher(secretKeyHex)
if err != nil {
return nil, err
}
mode, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
var nonce = make([]byte, mode.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
return mode.Seal(nonce, nonce, data, nil), nil
}
func (c *client) sign(secretKey string, values url.Values, body string) string {
var pList = make([]string, 0, 3+len(values))
for key := range values {
pList = append(pList, key+values.Get(key))
}
sort.Strings(pList)
var data = secretKey + strings.Join(pList, "") + body
var h = sha256.New()
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}