package core import ( "bytes" "context" "fmt" "gitea.cdlsxd.cn/sdk/plugin/utils" "github.com/go-playground/validator/v10" "io/ioutil" "net/http" "strings" "time" ) const ( DeBug = true SignType = "md5" SignKeyName = "Authorization" ApplicationJSON = "application/json" ) // DctWRequest 直连天下 Direct connection The world type DctWRequest interface { Json() ([]byte, error) Validate() error } type DctWConfig struct { AppId string `json:"app_id" validate:"required"` AppKey string `json:"app_key" validate:"required"` BaseUri string `json:"base_uri" validate:"required"` } func (c *DctWConfig) Validate() error { if err := validator.New().Struct(c); err != nil { for _, err = range err.(validator.ValidationErrors) { return err } } return nil } type DctWServer struct { DeBug bool Config *DctWConfig HttpClient *http.Client } type Option func(*DctWServer) func WithHttpClient(client *http.Client) Option { return func(s *DctWServer) { s.HttpClient = client } } func WithDebug(debug bool) Option { return func(s *DctWServer) { s.DeBug = debug } } func NewDctWServer(config *DctWConfig, o ...Option) (*DctWServer, error) { if err := config.Validate(); err != nil { return nil, err } httpClient := &http.Client{ Timeout: 15 * time.Second, } server := &DctWServer{ DeBug: DeBug, Config: config, HttpClient: httpClient, } for _, f := range o { f(server) } return server, nil } func (c *DctWServer) Sign(body, path, timestamp string) string { str := c.Config.AppId + body + path + timestamp + c.Config.AppKey return utils.Md5([]byte(str)) } func (c *DctWServer) BuildAuth(signStr, timestamp string) string { return fmt.Sprintf("%s appid=%s,timestamp=%s,sign=%s", SignType, c.Config.AppId, timestamp, signStr) } func (c *DctWServer) Headers(signStr, timestamp string) http.Header { h := make(http.Header) h.Add("Content-type", ApplicationJSON) h.Add(SignKeyName, c.BuildAuth(signStr, timestamp)) return h } func (c *DctWServer) verify(authorization, path string, body []byte) error { trimmedAuth := strings.TrimPrefix(authorization, "MD5 ") authData := make(map[string]string) parts := strings.Split(trimmedAuth, ",") for _, part := range parts { keyValue := strings.Split(part, "=") if len(keyValue) != 2 { return fmt.Errorf("授权信息格式错误: %s", part) } authData[keyValue[0]] = keyValue[1] } sign, ok := authData["sign"] if !ok { return fmt.Errorf("签名数据获取失败") } timestamp, ok := authData["timestamp"] if !ok { return fmt.Errorf("时间戳获取失败") } calculatedSign := c.Sign(string(body), path, timestamp) if sign == calculatedSign { return nil } return fmt.Errorf("验签失败") } func (c *DctWServer) VerifyDctW(authorization string, body []byte) error { trimmedAuth := strings.TrimPrefix(authorization, "MD5 ") authData := make(map[string]string) parts := strings.Split(trimmedAuth, ",") for _, part := range parts { keyValue := strings.Split(part, "=") if len(keyValue) != 2 { return fmt.Errorf("授权信息格式错误: %s", part) } authData[keyValue[0]] = keyValue[1] } sign, ok := authData["sign"] if !ok { return fmt.Errorf("签名数据获取失败") } signStr := fmt.Sprintf("%s%s%s", c.Config.AppId, string(body), c.Config.AppKey) calculatedSign := utils.Md5([]byte(signStr)) if sign == calculatedSign { return nil } return fmt.Errorf("验签失败") } func (c *DctWServer) Request(_ context.Context, request DctWRequest, path string) ([]byte, error) { timestamp := fmt.Sprintf("%d", time.Now().Unix()) body, err := request.Json() if err != nil { return nil, err } signStr := c.Sign(string(body), path, timestamp) url := fmt.Sprintf("%s%s", c.Config.BaseUri, path) httpClient := &http.Client{ Timeout: 15 * time.Second, } req, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) if err != nil { return nil, err } req.Header = c.Headers(signStr, timestamp) if c.DeBug { fmt.Printf("url:%s\n", url) fmt.Printf("body:%s\n", string(body)) fmt.Printf("signStr:%s\n", signStr) fmt.Printf("Header:%+v\n", req.Header) } resp, err := httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } if c.DeBug { fmt.Printf("resp.Status:%s\n", resp.Status) fmt.Printf("bodyStr:%s\n", string(bodyBytes)) } return bodyBytes, nil }