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  }