package v1 import ( "bytes" "crypto/md5" "encoding/hex" "encoding/json" "errors" "fmt" "io/ioutil" "metawant.greentech.com.cn/gaoyagang/gt-common/datacenter_client" "metawant.greentech.com.cn/gaoyagang/gt-common/httplib" "net/http" "net/url" "time" ) type ( DcApi struct { options ClientOptions } // ClientOptions 客户端配置选项 ClientOptions struct { httplib.HTTPSettings ServerIp string AppName string AppSecret string DoBefore []func(r *httplib.HTTPRequest) error DoAfter []func(r *httplib.HTTPRequest) error } ) func NewDcApi(options ClientOptions) *DcApi { dcapi := &DcApi{} if len(options.DoBefore) == 0 { options.DoBefore = make([]func(r *httplib.HTTPRequest) error, 0) } options.DoBefore = append(options.DoBefore, dcapi.signMiddleware) dcapi.options = options return dcapi } // SetHttpSettings 设置客户端选择 // ShowDebug bool // UserAgent string // TLSClientConfig *tls.Config // Proxy func(*http.Request) (*url.URL, error) // Transport http.RoundTripper // CheckRedirect func(req *http.Request, via []*http.Request) error // EnableCookie bool // Gzip bool // DumpBody bool // Retries int // if set to -1 means will retry forever // ConnectTimeout time.Duration // KeepAlive time.Duration // Timeout time.Duration func (d *DcApi) SetHttpSettings(settings httplib.HTTPSettings) { d.options.HTTPSettings = settings } func (d *DcApi) serviceUrl(serviceName string) string { return fmt.Sprintf("%s%s%s%s", "http://", d.options.ServerIp, "/api/dtgateway/v1", serviceName) } // 实际执行请求 func (d *DcApi) call(r *httplib.HTTPRequest, resp any) error { for _, bf := range d.options.DoBefore { if err := bf(r); err != nil { return err } } response, err := r.Response() if err != nil { return err } if response.StatusCode != http.StatusOK { msg, _ := r.String() return errors.New(fmt.Sprintf("response status code: %d, body: %s", response.StatusCode, msg)) } if xerr := r.ToJSON(resp); err != nil { return errors.New(fmt.Sprintf("response to json error %s", xerr.Error())) } // 这里应该增加一个记录日志的after for _, af := range d.options.DoAfter { if err := af(r); err != nil { return err } } return nil } // 数据签名middleware func (d *DcApi) signMiddleware(r *httplib.HTTPRequest) error { // 验证公共参数 if d.options.AppName == "" { return errors.New(fmt.Sprintf("error: %d app name empty", datacenter_client.DC_PARAMS_APP_NAME_MISSION)) } if d.options.AppSecret == "" { return errors.New(fmt.Sprintf("error: %d app secret empty", datacenter_client.DC_PARAMS_APP_SECRET_MISSION)) } queryParams := r.GetQueryParam() if pids, ok := queryParams["project_id"]; !ok || len(pids) == 0 { return errors.New(fmt.Sprintf("error: %d project id empty", datacenter_client.DC_PARAMS_PROJECT_ID_MISSION)) } else { r.Param("project_id", pids[0]) } r.Param("ts", fmt.Sprintf("%d", time.Now().Unix())) sign, err := CalcSign(r, d.options.AppSecret) if err != nil { return errors.New(fmt.Sprintf("error: %d check data sign error", datacenter_client.DC_DATA_SIGN_ERROR)) } r.Param("sign", sign) // 设置headers信息 r.Header("APP-NAME", d.options.AppName) return nil } func cutS2(s2 string) string { if s2 == "" { return "" } s2l := len(s2) //if s2l <= S2_MIN_LENGTH { // return s2 //} return fmt.Sprintf("%s%s", s2[:datacenter_client.S2_HEAD_LENGTH], s2[s2l-datacenter_client.S2_TAIL_LENGTH:]) } func sortS2(s2 string) (string, error) { if s2 == "" { return "", nil } var mi map[string]interface{} if err := json.Unmarshal([]byte(s2), &mi); err != nil { return "", err } //smi := SortMapByKey(mi) if bs, err := json.Marshal(mi); err != nil { return "", err } else { return string(bs), nil } } func CalcSign(r *httplib.HTTPRequest, secret string) (string, error) { s2, err := parseBody(r) if err != nil { fmt.Println("parseBody Error: ", err.Error()) return "", err } contentLength := r.GetRequest().ContentLength if contentLength > datacenter_client.S2_MIN_LENGTH { r.Param("sign_flag", "1") s2 = cutS2(s2) } else { s2, err = sortS2(s2) if err != nil { return "", err } } s3 := contentLength s1 := parseQuery(r) m := md5.New() m.Write([]byte(fmt.Sprintf("%s%s%d%s", s1, s2, s3, secret))) md5Str := hex.EncodeToString(m.Sum(nil)) //fmt.Println("s1", s1) //fmt.Println("s2", s2) //fmt.Println("s3", s3) //fmt.Println("md5", md5Str) return md5Str, nil } func parseBody(r *httplib.HTTPRequest) (s2 string, err error) { if r.GetRequest().Method == http.MethodGet { return "", nil } body := r.GetRequest().Body cnt, err := ioutil.ReadAll(body) if err != nil { return "", err } r.GetRequest().Body = ioutil.NopCloser(bytes.NewReader(cnt)) return string(cnt), nil } func parseQuery(r *httplib.HTTPRequest) string { params := r.GetQueryParam() delete(params, "sign") val := url.Values{} for k, v := range params { for index, s := range v { if index == 0 { val.Set(k, s) } else { val.Add(k, s) } } } return val.Encode() }