github.com/godaddy-x/freego@v1.0.156/utils/sdk/http.go (about) 1 package sdk 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "github.com/godaddy-x/eccrypto" 7 "github.com/godaddy-x/freego/ex" 8 "github.com/godaddy-x/freego/node" 9 "github.com/godaddy-x/freego/utils" 10 "github.com/valyala/fasthttp" 11 "time" 12 ) 13 14 type AuthToken struct { 15 Token string `json:"token"` 16 Secret string `json:"secret"` 17 Expired int64 `json:"expired"` 18 } 19 20 type HttpSDK struct { 21 Debug bool 22 Domain string 23 AuthDomain string 24 KeyPath string 25 LoginPath string 26 language string 27 timeout int64 28 authObject interface{} 29 authToken AuthToken 30 } 31 32 // 请使用指针对象 33 func (s *HttpSDK) AuthObject(object interface{}) { 34 s.authObject = object 35 } 36 37 func (s *HttpSDK) AuthToken(object AuthToken) { 38 s.authToken = object 39 } 40 41 func (s *HttpSDK) SetTimeout(timeout int64) { 42 s.timeout = timeout 43 } 44 45 func (s *HttpSDK) SetLanguage(language string) { 46 s.language = language 47 } 48 49 func (s *HttpSDK) debugOut(a ...interface{}) { 50 if !s.Debug { 51 return 52 } 53 fmt.Println(a...) 54 } 55 56 func (s *HttpSDK) getURI(path string) string { 57 if s.KeyPath == path || s.LoginPath == path { 58 return s.AuthDomain + path 59 } 60 return s.Domain + path 61 } 62 63 func (s *HttpSDK) GetPublicKey() (string, error) { 64 request := fasthttp.AcquireRequest() 65 request.Header.SetMethod("GET") 66 defer fasthttp.ReleaseRequest(request) 67 response := fasthttp.AcquireResponse() 68 defer fasthttp.ReleaseResponse(response) 69 _, b, err := fasthttp.Get(nil, s.getURI(s.KeyPath)) 70 if err != nil { 71 return "", ex.Throw{Msg: "request public key failed"} 72 } 73 if len(b) == 0 { 74 return "", ex.Throw{Msg: "request public key invalid"} 75 } 76 return utils.Bytes2Str(b), nil 77 } 78 79 // 对象请使用指针 80 func (s *HttpSDK) PostByECC(path string, requestObj, responseObj interface{}) error { 81 if len(path) == 0 || requestObj == nil || responseObj == nil { 82 return ex.Throw{Msg: "params invalid"} 83 } 84 jsonData, err := utils.JsonMarshal(requestObj) 85 if err != nil { 86 return ex.Throw{Msg: "request data JsonMarshal invalid"} 87 } 88 jsonBody := &node.JsonBody{ 89 Data: jsonData, 90 Time: utils.UnixSecond(), 91 Nonce: utils.RandNonce(), 92 Plan: int64(2), 93 } 94 publicKey, err := s.GetPublicKey() 95 if err != nil { 96 return err 97 } 98 clientSecretKey := utils.RandStr(24) 99 _, pubBs, err := ecc.LoadBase64PublicKey(publicKey) 100 if err != nil { 101 return ex.Throw{Msg: "load ECC public key failed"} 102 } 103 r, err := ecc.Encrypt(pubBs, utils.Str2Bytes(clientSecretKey)) 104 if err != nil { 105 return ex.Throw{Msg: "ECC encrypt failed"} 106 } 107 randomCode := base64.StdEncoding.EncodeToString(r) 108 s.debugOut("server key: ", publicKey) 109 s.debugOut("client key: ", clientSecretKey) 110 s.debugOut("client key encrypted: ", randomCode) 111 d, err := utils.AesEncrypt(jsonBody.Data.([]byte), clientSecretKey, clientSecretKey) 112 if err != nil { 113 return ex.Throw{Msg: "request data AES encrypt failed"} 114 } 115 jsonBody.Data = d 116 jsonBody.Sign = utils.HMAC_SHA256(utils.AddStr(path, jsonBody.Data.(string), jsonBody.Nonce, jsonBody.Time, jsonBody.Plan), publicKey, true) 117 bytesData, err := utils.JsonMarshal(jsonBody) 118 if err != nil { 119 return ex.Throw{Msg: "jsonBody data JsonMarshal invalid"} 120 } 121 s.debugOut("request data: ") 122 s.debugOut(utils.Bytes2Str(bytesData)) 123 request := fasthttp.AcquireRequest() 124 request.Header.SetContentType("application/json;charset=UTF-8") 125 request.Header.Set("Authorization", "") 126 request.Header.Set("RandomCode", randomCode) 127 request.Header.Set("Language", s.language) 128 request.Header.SetMethod("POST") 129 request.SetRequestURI(s.getURI(path)) 130 request.SetBody(bytesData) 131 defer fasthttp.ReleaseRequest(request) 132 response := fasthttp.AcquireResponse() 133 defer fasthttp.ReleaseResponse(response) 134 timeout := 120 * time.Second 135 if s.timeout > 0 { 136 timeout = time.Duration(s.timeout) * time.Second 137 } 138 if err := fasthttp.DoTimeout(request, response, timeout); err != nil { 139 return ex.Throw{Msg: "post request failed: " + err.Error()} 140 } 141 respBytes := response.Body() 142 s.debugOut("response data: ") 143 s.debugOut(utils.Bytes2Str(respBytes)) 144 respData := &node.JsonResp{ 145 Code: utils.GetJsonInt(respBytes, "c"), 146 Message: utils.GetJsonString(respBytes, "m"), 147 Data: utils.GetJsonString(respBytes, "d"), 148 Nonce: utils.GetJsonString(respBytes, "n"), 149 Time: int64(utils.GetJsonInt(respBytes, "t")), 150 Plan: int64(utils.GetJsonInt(respBytes, "p")), 151 Sign: utils.GetJsonString(respBytes, "s"), 152 } 153 if respData.Code != 200 { 154 if !utils.JsonValid(respBytes) && len(respData.Message) == 0 { 155 return ex.Throw{Msg: utils.Bytes2Str(respBytes)} 156 } 157 if respData.Code > 0 { 158 return ex.Throw{Code: respData.Code, Msg: respData.Message} 159 } 160 return ex.Throw{Msg: respData.Message} 161 } 162 validSign := utils.HMAC_SHA256(utils.AddStr(path, respData.Data, respData.Nonce, respData.Time, respData.Plan), clientSecretKey, true) 163 if validSign != respData.Sign { 164 return ex.Throw{Msg: "post response sign verify invalid"} 165 } 166 s.debugOut("response sign verify: ", validSign == respData.Sign) 167 dec, err := utils.AesDecrypt(respData.Data.(string), clientSecretKey, clientSecretKey) 168 if err != nil { 169 return ex.Throw{Msg: "post response data AES decrypt failed"} 170 } 171 s.debugOut("response data decrypted: ", utils.Bytes2Str(dec)) 172 if err := utils.JsonUnmarshal(dec, responseObj); err != nil { 173 return ex.Throw{Msg: "response data JsonUnmarshal invalid"} 174 } 175 return nil 176 } 177 178 func (s *HttpSDK) PostByHAX(path string, requestObj, responseObj interface{}) error { 179 if len(path) == 0 || requestObj == nil || responseObj == nil { 180 return ex.Throw{Msg: "params invalid"} 181 } 182 jsonData, err := utils.JsonMarshal(requestObj) 183 if err != nil { 184 return ex.Throw{Msg: "request data JsonMarshal invalid"} 185 } 186 jsonBody := &node.JsonBody{ 187 Data: utils.Base64Encode(jsonData), 188 Time: utils.UnixSecond(), 189 Nonce: utils.RandNonce(), 190 Plan: int64(3), 191 } 192 publicKey, err := s.GetPublicKey() 193 if err != nil { 194 return err 195 } 196 s.debugOut("server key: ", publicKey) 197 jsonBody.Sign = utils.HMAC_SHA256(utils.AddStr(path, jsonBody.Data.(string), jsonBody.Nonce, jsonBody.Time, jsonBody.Plan), publicKey, true) 198 bytesData, err := utils.JsonMarshal(jsonBody) 199 if err != nil { 200 return ex.Throw{Msg: "jsonBody data JsonMarshal invalid"} 201 } 202 s.debugOut("request data: ") 203 s.debugOut(utils.Bytes2Str(bytesData)) 204 request := fasthttp.AcquireRequest() 205 request.Header.SetContentType("application/json;charset=UTF-8") 206 request.Header.Set("Authorization", "") 207 request.Header.Set("Language", s.language) 208 request.Header.SetMethod("POST") 209 request.SetRequestURI(s.getURI(path)) 210 request.SetBody(bytesData) 211 defer fasthttp.ReleaseRequest(request) 212 response := fasthttp.AcquireResponse() 213 defer fasthttp.ReleaseResponse(response) 214 timeout := 120 * time.Second 215 if s.timeout > 0 { 216 timeout = time.Duration(s.timeout) * time.Second 217 } 218 if err := fasthttp.DoTimeout(request, response, timeout); err != nil { 219 return ex.Throw{Msg: "post request failed: " + err.Error()} 220 } 221 respBytes := response.Body() 222 s.debugOut("response data: ") 223 s.debugOut(utils.Bytes2Str(respBytes)) 224 respData := &node.JsonResp{ 225 Code: utils.GetJsonInt(respBytes, "c"), 226 Message: utils.GetJsonString(respBytes, "m"), 227 Data: utils.GetJsonString(respBytes, "d"), 228 Nonce: utils.GetJsonString(respBytes, "n"), 229 Time: int64(utils.GetJsonInt(respBytes, "t")), 230 Plan: int64(utils.GetJsonInt(respBytes, "p")), 231 Sign: utils.GetJsonString(respBytes, "s"), 232 } 233 if respData.Code != 200 { 234 if respData.Code > 0 { 235 return ex.Throw{Code: respData.Code, Msg: respData.Message} 236 } 237 return ex.Throw{Msg: respData.Message} 238 } 239 validSign := utils.HMAC_SHA256(utils.AddStr(path, respData.Data, respData.Nonce, respData.Time, respData.Plan), publicKey, true) 240 if validSign != respData.Sign { 241 return ex.Throw{Msg: "post response sign verify invalid"} 242 } 243 s.debugOut("response sign verify: ", validSign == respData.Sign) 244 dec := utils.Base64Decode(respData.Data) 245 s.debugOut("response data base64: ", string(dec)) 246 if err := utils.JsonUnmarshal(dec, responseObj); err != nil { 247 return ex.Throw{Msg: "response data JsonUnmarshal invalid"} 248 } 249 return nil 250 } 251 252 func (s *HttpSDK) valid() bool { 253 if len(s.authToken.Token) == 0 { 254 return false 255 } 256 if len(s.authToken.Secret) == 0 { 257 return false 258 } 259 if utils.UnixSecond() > s.authToken.Expired-3600 { 260 return false 261 } 262 return true 263 } 264 265 func (s *HttpSDK) checkAuth() error { 266 if s.valid() { 267 return nil 268 } 269 if s.authObject == nil { // 没授权对象则忽略 270 return nil 271 } 272 if len(s.Domain) == 0 { 273 return ex.Throw{Msg: "domain is nil"} 274 } 275 if len(s.KeyPath) == 0 { 276 return ex.Throw{Msg: "keyPath is nil"} 277 } 278 if len(s.LoginPath) == 0 { 279 return ex.Throw{Msg: "loginPath is nil"} 280 } 281 if s.authObject == nil { 282 return ex.Throw{Msg: "authObject is nil"} 283 } 284 responseObj := AuthToken{} 285 if err := s.PostByECC(s.LoginPath, s.authObject, &responseObj); err != nil { 286 return err 287 } 288 s.AuthToken(responseObj) 289 return nil 290 } 291 292 // PostByAuth 对象请使用指针 293 func (s *HttpSDK) PostByAuth(path string, requestObj, responseObj interface{}, encrypted ...bool) error { 294 if len(path) == 0 || requestObj == nil || responseObj == nil { 295 return ex.Throw{Msg: "params invalid"} 296 } 297 if err := s.checkAuth(); err != nil { 298 return err 299 } 300 if len(s.authToken.Token) == 0 || len(s.authToken.Secret) == 0 { 301 return ex.Throw{Msg: "token or secret can't be empty"} 302 } 303 jsonData, err := utils.JsonMarshal(requestObj) 304 if err != nil { 305 return ex.Throw{Msg: "request data JsonMarshal invalid"} 306 } 307 jsonBody := &node.JsonBody{ 308 Data: jsonData, 309 Time: utils.UnixSecond(), 310 Nonce: utils.RandNonce(), 311 Plan: 0, 312 } 313 if len(encrypted) > 0 && encrypted[0] { 314 d, err := utils.AesEncrypt(jsonBody.Data.([]byte), s.authToken.Secret, utils.AddStr(jsonBody.Nonce, jsonBody.Time)) 315 if err != nil { 316 return ex.Throw{Msg: "request data AES encrypt failed"} 317 } 318 jsonBody.Data = d 319 jsonBody.Plan = 1 320 s.debugOut("request data encrypted: ", jsonBody.Data) 321 } else { 322 d := utils.Base64Encode(jsonBody.Data.([]byte)) 323 jsonBody.Data = d 324 s.debugOut("request data base64: ", jsonBody.Data) 325 } 326 jsonBody.Sign = utils.HMAC_SHA256(utils.AddStr(path, jsonBody.Data.(string), jsonBody.Nonce, jsonBody.Time, jsonBody.Plan), s.authToken.Secret, true) 327 bytesData, err := utils.JsonMarshal(jsonBody) 328 if err != nil { 329 return ex.Throw{Msg: "jsonBody data JsonMarshal invalid"} 330 } 331 s.debugOut("request data: ") 332 s.debugOut(utils.Bytes2Str(bytesData)) 333 request := fasthttp.AcquireRequest() 334 request.Header.SetContentType("application/json;charset=UTF-8") 335 request.Header.Set("Authorization", s.authToken.Token) 336 request.Header.Set("Language", s.language) 337 request.Header.SetMethod("POST") 338 request.SetRequestURI(s.getURI(path)) 339 request.SetBody(bytesData) 340 defer fasthttp.ReleaseRequest(request) 341 response := fasthttp.AcquireResponse() 342 defer fasthttp.ReleaseResponse(response) 343 timeout := 120 * time.Second 344 if s.timeout > 0 { 345 timeout = time.Duration(s.timeout) * time.Second 346 } 347 if err := fasthttp.DoTimeout(request, response, timeout); err != nil { 348 return ex.Throw{Msg: "post request failed: " + err.Error()} 349 } 350 respBytes := response.Body() 351 s.debugOut("response data: ") 352 s.debugOut(utils.Bytes2Str(respBytes)) 353 respData := &node.JsonResp{ 354 Code: utils.GetJsonInt(respBytes, "c"), 355 Message: utils.GetJsonString(respBytes, "m"), 356 Data: utils.GetJsonString(respBytes, "d"), 357 Nonce: utils.GetJsonString(respBytes, "n"), 358 Time: int64(utils.GetJsonInt(respBytes, "t")), 359 Plan: int64(utils.GetJsonInt(respBytes, "p")), 360 Sign: utils.GetJsonString(respBytes, "s"), 361 } 362 if respData.Code != 200 { 363 if respData.Code > 0 { 364 return ex.Throw{Code: respData.Code, Msg: respData.Message} 365 } 366 return ex.Throw{Msg: respData.Message} 367 } 368 validSign := utils.HMAC_SHA256(utils.AddStr(path, respData.Data, respData.Nonce, respData.Time, respData.Plan), s.authToken.Secret, true) 369 if validSign != respData.Sign { 370 return ex.Throw{Msg: "post response sign verify invalid"} 371 } 372 s.debugOut("response sign verify: ", validSign == respData.Sign) 373 var dec []byte 374 if respData.Plan == 0 { 375 dec = utils.Base64Decode(respData.Data) 376 s.debugOut("response data base64: ", string(dec)) 377 } else if respData.Plan == 1 { 378 dec, err = utils.AesDecrypt(respData.Data.(string), s.authToken.Secret, utils.AddStr(respData.Nonce, respData.Time)) 379 if err != nil { 380 return ex.Throw{Msg: "post response data AES decrypt failed"} 381 } 382 s.debugOut("response data decrypted: ", utils.Bytes2Str(dec)) 383 } else { 384 return ex.Throw{Msg: "response sign plan invalid"} 385 } 386 if err := utils.JsonUnmarshal(dec, responseObj); err != nil { 387 return ex.Throw{Msg: "response data JsonUnmarshal invalid"} 388 } 389 return nil 390 } 391 392 func BuildRequestObject(path string, requestObj interface{}, secret string, encrypted ...bool) ([]byte, error) { 393 if len(path) == 0 || requestObj == nil { 394 return nil, ex.Throw{Msg: "params invalid"} 395 } 396 jsonData, err := utils.JsonMarshal(requestObj) 397 if err != nil { 398 return nil, ex.Throw{Msg: "request data JsonMarshal invalid"} 399 } 400 jsonBody := &node.JsonBody{ 401 Data: jsonData, 402 Time: utils.UnixSecond(), 403 Nonce: utils.RandNonce(), 404 Plan: 0, 405 } 406 if len(encrypted) > 0 && encrypted[0] { 407 d, err := utils.AesEncrypt(jsonBody.Data.([]byte), secret, utils.AddStr(jsonBody.Nonce, jsonBody.Time)) 408 if err != nil { 409 return nil, ex.Throw{Msg: "request data AES encrypt failed"} 410 } 411 jsonBody.Data = d 412 jsonBody.Plan = 1 413 } else { 414 d := utils.Base64Encode(jsonBody.Data.([]byte)) 415 jsonBody.Data = d 416 } 417 jsonBody.Sign = utils.HMAC_SHA256(utils.AddStr(path, jsonBody.Data.(string), jsonBody.Nonce, jsonBody.Time, jsonBody.Plan), secret, true) 418 bytesData, err := utils.JsonMarshal(jsonBody) 419 if err != nil { 420 return nil, ex.Throw{Msg: "jsonBody data JsonMarshal invalid"} 421 } 422 return bytesData, nil 423 }