197 lines
4.4 KiB
Go
197 lines
4.4 KiB
Go
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
|
|
}
|