mirror of
https://github.com/certimate-go/certimate.git
synced 2026-07-20 21:01:41 +08:00
refactor: refactor sdk3rd client implementations to use a unified signer structure
This commit is contained in:
parent
6826210c0d
commit
dcd0ac6916
@ -50,7 +50,7 @@ func NewNotifier(config *NotifierConfig) (*Notifier, error) {
|
||||
client := resty.New().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if config.Secret != "" {
|
||||
timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
|
||||
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
package onepanel
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@ -36,22 +34,23 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
apiKey: opts.ApiKey,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/api/v1").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
tokenMd5 := md5.Sum([]byte("1panel" + opts.ApiKey + timestamp))
|
||||
tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
|
||||
req.Header.Set("1Panel-Timestamp", timestamp)
|
||||
req.Header.Set("1Panel-Token", tokenMd5Hex)
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
28
pkg/sdk3rd/1panel/signer.go
Normal file
28
pkg/sdk3rd/1panel/signer.go
Normal file
@ -0,0 +1,28 @@
|
||||
package onepanel
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
apiKey string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://1panel.cn/docs/v1/dev_manual/api_manual/
|
||||
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
|
||||
tokenMd5 := md5.Sum([]byte("1panel" + s.apiKey + timestamp))
|
||||
tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
|
||||
|
||||
req.Header.Set("1Panel-Timestamp", timestamp)
|
||||
req.Header.Set("1Panel-Token", tokenMd5Hex)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -1,9 +1,7 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@ -36,23 +34,24 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
apiKey: opts.ApiKey,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/api/v2").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("CurrentNode", opts.CurrentNode).
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
tokenMd5 := md5.Sum([]byte("1panel" + opts.ApiKey + timestamp))
|
||||
tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
|
||||
req.Header.Set("1Panel-Timestamp", timestamp)
|
||||
req.Header.Set("1Panel-Token", tokenMd5Hex)
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
28
pkg/sdk3rd/1panel/v2/signer.go
Normal file
28
pkg/sdk3rd/1panel/v2/signer.go
Normal file
@ -0,0 +1,28 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
apiKey string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://1panel.cn/docs/v2/dev_manual/api_manual/
|
||||
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
|
||||
tokenMd5 := md5.Sum([]byte("1panel" + s.apiKey + timestamp))
|
||||
tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
|
||||
|
||||
req.Header.Set("1Panel-Timestamp", timestamp)
|
||||
req.Header.Set("1Panel-Token", tokenMd5Hex)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -1,19 +1,12 @@
|
||||
package oss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
@ -57,127 +50,25 @@ func NewClient(endpoint string, optFns ...OptionsFunc) (*Client, error) {
|
||||
}
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
accessKeyId: opts.AccessKeyId,
|
||||
accessKeySecret: opts.AccessKeySecret,
|
||||
region: opts.Region,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL(endpoint).
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://help.aliyun.com/zh/oss/developer-reference/recommend-to-use-signature-version-4
|
||||
// https://www.alibabacloud.com/help/en/oss/developer-reference/recommend-to-use-signature-version-4
|
||||
|
||||
method := strings.ToUpper(req.Method)
|
||||
|
||||
nowUtc := time.Now().UTC()
|
||||
headerDateStr := nowUtc.Format(http.TimeFormat)
|
||||
requestDateStr := nowUtc.Format("20060102T150405Z")
|
||||
signDateStr := nowUtc.Format("20060102")
|
||||
|
||||
requestResStr := req.Header.Get("X-API-Resource")
|
||||
req.Header.Del("X-API-Resource")
|
||||
|
||||
canonicalUrl := escapePath(req.URL.Path)
|
||||
if canonicalUrl == "" {
|
||||
canonicalUrl = "/"
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
if canonicalUrl == "/" && requestResStr != "" {
|
||||
canonicalUrl = escapePath(requestResStr)
|
||||
}
|
||||
|
||||
canonicalQueryStr := ""
|
||||
if len(req.URL.Query()) > 0 {
|
||||
query := req.URL.Query()
|
||||
|
||||
keys := make([]string, 0, len(query))
|
||||
for key := range query {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
canonicalQueryStr += "&"
|
||||
}
|
||||
|
||||
value := query.Get(key)
|
||||
if value == "" {
|
||||
canonicalQueryStr += escapeQuery(key)
|
||||
} else {
|
||||
canonicalQueryStr += escapeQuery(key) + "=" + escapeQuery(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canonicalHeaders := ""
|
||||
additionalHeaders := ""
|
||||
if len(req.Header) > 0 {
|
||||
if req.Header.Get("X-OSS-Date") == "" {
|
||||
req.Header.Set("X-OSS-Date", requestDateStr)
|
||||
}
|
||||
if req.Header.Get("X-OSS-Content-SHA256") == "" {
|
||||
req.Header.Set("X-OSS-Content-SHA256", "UNSIGNED-PAYLOAD")
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(req.Header))
|
||||
for key := range req.Header {
|
||||
key = strings.ToLower(key)
|
||||
if strings.HasPrefix(key, "x-oss-") {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
if key == "content-type" || key == "content-md5" {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
canonicalHeaders += "\n"
|
||||
}
|
||||
|
||||
value := strings.TrimSpace(req.Header.Get(key))
|
||||
canonicalHeaders += key + ":" + value
|
||||
}
|
||||
|
||||
canonicalHeaders += "\n"
|
||||
}
|
||||
|
||||
hashedPayload := req.Header.Get("X-OSS-Content-SHA256")
|
||||
|
||||
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", method, canonicalUrl, canonicalQueryStr, canonicalHeaders, additionalHeaders, hashedPayload)
|
||||
canonicalRequestHash := sha256.Sum256([]byte(canonicalRequest))
|
||||
canonicalRequestHashHex := strings.ToLower(hex.EncodeToString(canonicalRequestHash[:]))
|
||||
|
||||
const signAlgorithmHeader = "OSS4-HMAC-SHA256"
|
||||
scope := fmt.Sprintf("%s/%s/oss/aliyun_v4_request", signDateStr, opts.Region)
|
||||
stringToSign := fmt.Sprintf("%s\n%s\n%s\n%s", signAlgorithmHeader, requestDateStr, scope, canonicalRequestHashHex)
|
||||
|
||||
var h hash.Hash
|
||||
h = hmac.New(sha256.New, []byte("aliyun_v4"+opts.AccessKeySecret))
|
||||
h.Write([]byte(signDateStr))
|
||||
kDate := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kDate)
|
||||
h.Write([]byte(opts.Region))
|
||||
kRegion := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kRegion)
|
||||
h.Write([]byte("oss"))
|
||||
kService := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kService)
|
||||
h.Write([]byte("aliyun_v4_request"))
|
||||
kSigning := h.Sum(nil)
|
||||
|
||||
h = hmac.New(sha256.New, kSigning)
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
|
||||
|
||||
req.Header.Set("Authorization", fmt.Sprintf("%s Credential=%s/%s, Signature=%s", signAlgorithmHeader, opts.AccessKeyId, scope, signature))
|
||||
req.Header.Set("Date", headerDateStr)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{
|
||||
bucket: opts.Bucket,
|
||||
rc: restyClient,
|
||||
rc: httper,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -267,30 +158,3 @@ func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*rest
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func escapeQuery(str string) string {
|
||||
res := url.QueryEscape(str)
|
||||
res = strings.ReplaceAll(res, "+", "%20")
|
||||
return res
|
||||
}
|
||||
|
||||
func escapePath(path string) string {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(path); i++ {
|
||||
c := path[i]
|
||||
noEscape := (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' ||
|
||||
c == '.' ||
|
||||
c == '_' ||
|
||||
c == '~' ||
|
||||
c == '/'
|
||||
if noEscape {
|
||||
buf.WriteByte(c)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "%%%02X", c)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
165
pkg/sdk3rd/alibabacloud/oss/signer.go
Normal file
165
pkg/sdk3rd/alibabacloud/oss/signer.go
Normal file
@ -0,0 +1,165 @@
|
||||
package oss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
accessKeyId string
|
||||
accessKeySecret string
|
||||
region string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://help.aliyun.com/zh/oss/developer-reference/recommend-to-use-signature-version-4
|
||||
// https://www.alibabacloud.com/help/en/oss/developer-reference/recommend-to-use-signature-version-4
|
||||
|
||||
method := strings.ToUpper(req.Method)
|
||||
|
||||
nowUtc := time.Now().UTC()
|
||||
headerDateStr := nowUtc.Format(http.TimeFormat)
|
||||
requestDateStr := nowUtc.Format("20060102T150405Z")
|
||||
signDateStr := nowUtc.Format("20060102")
|
||||
|
||||
requestResStr := req.Header.Get("X-API-Resource")
|
||||
req.Header.Del("X-API-Resource")
|
||||
|
||||
canonicalUrl := escapePath(req.URL.Path)
|
||||
if canonicalUrl == "" {
|
||||
canonicalUrl = "/"
|
||||
}
|
||||
if canonicalUrl == "/" && requestResStr != "" {
|
||||
canonicalUrl = escapePath(requestResStr)
|
||||
}
|
||||
|
||||
canonicalQueryStr := ""
|
||||
if len(req.URL.Query()) > 0 {
|
||||
query := req.URL.Query()
|
||||
|
||||
keys := make([]string, 0, len(query))
|
||||
for key := range query {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
canonicalQueryStr += "&"
|
||||
}
|
||||
|
||||
value := query.Get(key)
|
||||
if value == "" {
|
||||
canonicalQueryStr += escapeQuery(key)
|
||||
} else {
|
||||
canonicalQueryStr += escapeQuery(key) + "=" + escapeQuery(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canonicalHeaders := ""
|
||||
additionalHeaders := ""
|
||||
if len(req.Header) > 0 {
|
||||
if req.Header.Get("X-OSS-Date") == "" {
|
||||
req.Header.Set("X-OSS-Date", requestDateStr)
|
||||
}
|
||||
if req.Header.Get("X-OSS-Content-SHA256") == "" {
|
||||
req.Header.Set("X-OSS-Content-SHA256", "UNSIGNED-PAYLOAD")
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(req.Header))
|
||||
for key := range req.Header {
|
||||
key = strings.ToLower(key)
|
||||
if strings.HasPrefix(key, "x-oss-") {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
if key == "content-type" || key == "content-md5" {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
canonicalHeaders += "\n"
|
||||
}
|
||||
|
||||
value := strings.TrimSpace(req.Header.Get(key))
|
||||
canonicalHeaders += key + ":" + value
|
||||
}
|
||||
|
||||
canonicalHeaders += "\n"
|
||||
}
|
||||
|
||||
hashedPayload := req.Header.Get("X-OSS-Content-SHA256")
|
||||
|
||||
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", method, canonicalUrl, canonicalQueryStr, canonicalHeaders, additionalHeaders, hashedPayload)
|
||||
canonicalRequestHash := sha256.Sum256([]byte(canonicalRequest))
|
||||
canonicalRequestHashHex := strings.ToLower(hex.EncodeToString(canonicalRequestHash[:]))
|
||||
|
||||
const signAlgorithmHeader = "OSS4-HMAC-SHA256"
|
||||
scope := fmt.Sprintf("%s/%s/oss/aliyun_v4_request", signDateStr, s.region)
|
||||
stringToSign := fmt.Sprintf("%s\n%s\n%s\n%s", signAlgorithmHeader, requestDateStr, scope, canonicalRequestHashHex)
|
||||
|
||||
var h hash.Hash
|
||||
h = hmac.New(sha256.New, []byte("aliyun_v4"+s.accessKeySecret))
|
||||
h.Write([]byte(signDateStr))
|
||||
kDate := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kDate)
|
||||
h.Write([]byte(s.region))
|
||||
kRegion := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kRegion)
|
||||
h.Write([]byte("oss"))
|
||||
kService := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kService)
|
||||
h.Write([]byte("aliyun_v4_request"))
|
||||
kSigning := h.Sum(nil)
|
||||
|
||||
h = hmac.New(sha256.New, kSigning)
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
|
||||
|
||||
authorization := fmt.Sprintf("%s Credential=%s/%s, Signature=%s", signAlgorithmHeader, s.accessKeyId, scope, signature)
|
||||
|
||||
req.Header.Set("Authorization", authorization)
|
||||
req.Header.Set("Date", headerDateStr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func escapeQuery(str string) string {
|
||||
res := url.QueryEscape(str)
|
||||
res = strings.ReplaceAll(res, "+", "%20")
|
||||
return res
|
||||
}
|
||||
|
||||
func escapePath(path string) string {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(path); i++ {
|
||||
c := path[i]
|
||||
noEscape := (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' ||
|
||||
c == '.' ||
|
||||
c == '_' ||
|
||||
c == '~' ||
|
||||
c == '/'
|
||||
if noEscape {
|
||||
buf.WriteByte(c)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "%%%02X", c)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
@ -33,14 +33,14 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/apisix/admin").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetHeader("X-Api-Key", opts.ApiKey)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -24,14 +24,14 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://cdn.api.baishan.com").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetQueryParam("token", opts.ApiToken)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -38,7 +38,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")).
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/x-www-form-urlencoded").
|
||||
@ -46,7 +46,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
|
||||
return &Client{
|
||||
apiKey: opts.ApiKey,
|
||||
rc: restyClient,
|
||||
rc: httper,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -96,9 +96,9 @@ func (c *Client) newRequest(method string, path string, params any) (*resty.Requ
|
||||
}
|
||||
}
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
data["request_time"] = fmt.Sprintf("%d", timestamp)
|
||||
data["request_token"] = generateSignature(fmt.Sprintf("%d", timestamp), c.apiKey)
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
data["request_time"] = timestamp
|
||||
data["request_token"] = generateSignature(timestamp, c.apiKey)
|
||||
|
||||
req := c.rc.R()
|
||||
req.Method = method
|
||||
|
||||
@ -39,7 +39,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")).
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/x-www-form-urlencoded").
|
||||
@ -47,7 +47,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
|
||||
return &Client{
|
||||
apiKey: opts.ApiKey,
|
||||
rc: restyClient,
|
||||
rc: httper,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -97,9 +97,9 @@ func (c *Client) newRequest(method string, path string, params any, multipart bo
|
||||
}
|
||||
}
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
data["request_time"] = fmt.Sprintf("%d", timestamp)
|
||||
data["request_token"] = generateSignature(fmt.Sprintf("%d", timestamp), c.apiKey)
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
data["request_time"] = timestamp
|
||||
data["request_token"] = generateSignature(timestamp, c.apiKey)
|
||||
|
||||
req := c.rc.R()
|
||||
req.Method = method
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
package btwaf
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@ -36,24 +34,23 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
apiKey: opts.ApiKey,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/api").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
keyMd5 := md5.Sum([]byte(opts.ApiKey))
|
||||
keyMd5Hex := strings.ToLower(hex.EncodeToString(keyMd5[:]))
|
||||
signMd5 := md5.Sum([]byte(timestamp + keyMd5Hex))
|
||||
signMd5Hex := strings.ToLower(hex.EncodeToString(signMd5[:]))
|
||||
req.Header.Set("waf_request_time", timestamp)
|
||||
req.Header.Set("waf_request_token", signMd5Hex)
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
32
pkg/sdk3rd/btwaf/signer.go
Normal file
32
pkg/sdk3rd/btwaf/signer.go
Normal file
@ -0,0 +1,32 @@
|
||||
package btwaf
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
apiKey string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://github.com/aaPanel/aaWAF/blob/main/API.md
|
||||
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
|
||||
keyMd5 := md5.Sum([]byte(s.apiKey))
|
||||
keyMd5Hex := strings.ToLower(hex.EncodeToString(keyMd5[:]))
|
||||
|
||||
signMd5 := md5.Sum([]byte(timestamp + keyMd5Hex))
|
||||
signMd5Hex := strings.ToLower(hex.EncodeToString(signMd5[:]))
|
||||
|
||||
req.Header.Set("waf_request_time", timestamp)
|
||||
req.Header.Set("waf_request_token", signMd5Hex)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -23,14 +23,14 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://api.bunny.net").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("AccessKey", opts.ApiKey).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -24,14 +24,14 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://api.cachefly.com/api/2.5").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetHeader("X-CF-Authorization", "Bearer "+opts.ApiToken)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -36,7 +36,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiSecret")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/v1").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("API-Key", opts.ApiKey).
|
||||
@ -44,7 +44,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -24,14 +24,14 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://api.cloudflare.com/client/v4").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+opts.ApiToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -54,7 +54,7 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if client.accessToken != "" {
|
||||
req.Header.Set("X-Auth-Token", client.accessToken)
|
||||
}
|
||||
|
||||
@ -36,13 +36,13 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/execute").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Authorization", "cpanel "+opts.Username+":"+opts.ApiToken).
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -1,21 +1,13 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/app"
|
||||
)
|
||||
@ -43,62 +35,24 @@ func NewClient(baseUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset secretAccessKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
accessKeyId: opts.AccessKeyId,
|
||||
secretAccessKey: opts.SecretAccessKey,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL(baseUrl).
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=77&api=%u6784%u9020%u8BF7%u6C42&data=114&vid=107
|
||||
|
||||
now := time.Now()
|
||||
eopDate := now.Format("20060102T150405Z")
|
||||
eopReqId := security.RandomString(32)
|
||||
|
||||
queryStr := ""
|
||||
if req.URL != nil {
|
||||
queryStr = req.URL.Query().Encode()
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
|
||||
payloadStr := ""
|
||||
if req.Method != http.MethodGet && req.Body != nil {
|
||||
payloadb, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payloadStr = string(payloadb)
|
||||
req.Body = io.NopCloser(bytes.NewReader(payloadb))
|
||||
}
|
||||
payloadHash := sha256.Sum256([]byte(payloadStr))
|
||||
payloadHashHex := hex.EncodeToString(payloadHash[:])
|
||||
|
||||
var h hash.Hash
|
||||
h = hmac.New(sha256.New, []byte(opts.SecretAccessKey))
|
||||
h.Write([]byte(eopDate))
|
||||
kTime := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kTime)
|
||||
h.Write([]byte(opts.AccessKeyId))
|
||||
kAk := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kAk)
|
||||
h.Write([]byte(now.Format("20060102")))
|
||||
kDate := h.Sum(nil)
|
||||
|
||||
stringToSign := fmt.Sprintf("ctyun-eop-request-id:%s\neop-date:%s\n\n%s\n%s", eopReqId, eopDate, queryStr, payloadHashHex)
|
||||
|
||||
h = hmac.New(sha256.New, kDate)
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
|
||||
req.Header.Set("ctyun-eop-request-id", eopReqId)
|
||||
req.Header.Set("eop-date", eopDate)
|
||||
req.Header.Set("eop-authorization", fmt.Sprintf("%s Headers=ctyun-eop-request-id;eop-date Signature=%s", opts.AccessKeyId, signature))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
73
pkg/sdk3rd/ctyun/openapi/signer.go
Normal file
73
pkg/sdk3rd/ctyun/openapi/signer.go
Normal file
@ -0,0 +1,73 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
accessKeyId string
|
||||
secretAccessKey string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=77&api=%u6784%u9020%u8BF7%u6C42&data=114&vid=107
|
||||
|
||||
now := time.Now()
|
||||
eopDate := now.Format("20060102T150405Z")
|
||||
eopReqId := security.RandomString(32)
|
||||
|
||||
queryStr := ""
|
||||
if req.URL != nil {
|
||||
queryStr = req.URL.Query().Encode()
|
||||
}
|
||||
|
||||
payloadStr := ""
|
||||
if req.Method != http.MethodGet && req.Body != nil {
|
||||
payloadb, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payloadStr = string(payloadb)
|
||||
req.Body = io.NopCloser(bytes.NewReader(payloadb))
|
||||
}
|
||||
payloadHash := sha256.Sum256([]byte(payloadStr))
|
||||
payloadHashHex := hex.EncodeToString(payloadHash[:])
|
||||
|
||||
var h hash.Hash
|
||||
h = hmac.New(sha256.New, []byte(s.secretAccessKey))
|
||||
h.Write([]byte(eopDate))
|
||||
kTime := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kTime)
|
||||
h.Write([]byte(s.accessKeyId))
|
||||
kAk := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kAk)
|
||||
h.Write([]byte(now.Format("20060102")))
|
||||
kDate := h.Sum(nil)
|
||||
|
||||
stringToSign := fmt.Sprintf("ctyun-eop-request-id:%s\neop-date:%s\n\n%s\n%s", eopReqId, eopDate, queryStr, payloadHashHex)
|
||||
|
||||
h = hmac.New(sha256.New, kDate)
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
|
||||
eopAuthorization := fmt.Sprintf("%s Headers=ctyun-eop-request-id;eop-date Signature=%s", s.accessKeyId, signature)
|
||||
|
||||
req.Header.Set("ctyun-eop-request-id", eopReqId)
|
||||
req.Header.Set("eop-date", eopDate)
|
||||
req.Header.Set("eop-authorization", eopAuthorization)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -72,7 +72,7 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if client.apiUserToken != "" {
|
||||
req.Header.Set("Token", client.apiUserToken)
|
||||
}
|
||||
|
||||
@ -24,14 +24,14 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset accessToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://api.digitalocean.com/v2").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+opts.AccessToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -1,13 +1,8 @@
|
||||
package dogecloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@ -33,44 +28,24 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset secretKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
accessKey: opts.AccessKey,
|
||||
secretKey: opts.SecretKey,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://api.dogecloud.com").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(ctx *resty.Client, req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://docs.dogecloud.com/cdn/api-access-token
|
||||
|
||||
path := req.URL.Path
|
||||
queryStr := req.URL.Query().Encode()
|
||||
if queryStr != "" {
|
||||
path += "?" + queryStr
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
|
||||
payloadStr := ""
|
||||
if req.Body != nil {
|
||||
payloadb, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payloadStr = string(payloadb)
|
||||
req.Body = io.NopCloser(bytes.NewReader(payloadb))
|
||||
}
|
||||
|
||||
stringToSign := fmt.Sprintf("%s\n%s", path, payloadStr)
|
||||
|
||||
h := hmac.New(sha1.New, []byte(opts.SecretKey))
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
req.Header.Set("Authorization", fmt.Sprintf("TOKEN %s:%s", opts.AccessKey, signature))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{restyClient}, nil
|
||||
return &Client{httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
50
pkg/sdk3rd/dogecloud/signer.go
Normal file
50
pkg/sdk3rd/dogecloud/signer.go
Normal file
@ -0,0 +1,50 @@
|
||||
package dogecloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
accessKey string
|
||||
secretKey string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://docs.dogecloud.com/cdn/api-access-token
|
||||
|
||||
path := req.URL.Path
|
||||
queryStr := req.URL.Query().Encode()
|
||||
if queryStr != "" {
|
||||
path += "?" + queryStr
|
||||
}
|
||||
|
||||
payloadStr := ""
|
||||
if req.Body != nil {
|
||||
payloadb, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payloadStr = string(payloadb)
|
||||
req.Body = io.NopCloser(bytes.NewReader(payloadb))
|
||||
}
|
||||
|
||||
stringToSign := fmt.Sprintf("%s\n%s", path, payloadStr)
|
||||
|
||||
h := hmac.New(sha1.New, []byte(s.secretKey))
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
authorization := fmt.Sprintf("TOKEN %s:%s", s.accessKey, signature)
|
||||
|
||||
req.Header.Set("Authorization", authorization)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -33,14 +33,14 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/api").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetHeader("X-Api-Key", opts.ApiKey)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -24,14 +24,14 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset httpToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://dynv6.com/api/v2").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+opts.HttpToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -62,7 +62,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if client.accessToken != "" {
|
||||
req.Header.Set("X-Cloud-Access-Token", client.accessToken)
|
||||
}
|
||||
|
||||
@ -24,14 +24,14 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://api.machines.dev/v1").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+opts.ApiToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -62,7 +62,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if client.accessToken != "" {
|
||||
req.Header.Set("X-Edge-Access-Token", client.accessToken)
|
||||
}
|
||||
|
||||
@ -1,17 +1,12 @@
|
||||
package obs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
@ -53,82 +48,23 @@ func NewClient(endpoint string, optFns ...OptionsFunc) (*Client, error) {
|
||||
}
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
accessKeyId: opts.AccessKeyId,
|
||||
secretAccessKey: opts.SecretAccessKey,
|
||||
bucket: opts.Bucket,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL(endpoint).
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://support.huaweicloud.com/api-obs/obs_04_0010.html
|
||||
|
||||
canonicalizedHeaders := ""
|
||||
if len(req.Header) > 0 {
|
||||
keys := make([]string, 0, len(req.Header))
|
||||
for key := range req.Header {
|
||||
key = strings.ToLower(key)
|
||||
if strings.HasPrefix(key, "x-obs-") {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
value := strings.TrimSpace(req.Header.Get(key))
|
||||
canonicalizedHeaders += key + ":" + escapeQuery(value)
|
||||
canonicalizedHeaders += "\n"
|
||||
}
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
|
||||
bucketName := opts.Bucket
|
||||
objectName := strings.Trim(req.URL.Path, "/")
|
||||
canonicalizedResources := escapePath(fmt.Sprintf("/%s/%s", bucketName, objectName))
|
||||
if bucketName == "" && objectName == "" {
|
||||
canonicalizedResources = "/"
|
||||
}
|
||||
if len(req.URL.Query()) > 0 {
|
||||
query := req.URL.Query()
|
||||
|
||||
keys := make([]string, 0, len(query))
|
||||
for key := range query {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i == 0 {
|
||||
canonicalizedResources += "?"
|
||||
} else {
|
||||
canonicalizedResources += "&"
|
||||
}
|
||||
|
||||
value := query.Get(key)
|
||||
if value == "" {
|
||||
canonicalizedResources += escapeQuery(key)
|
||||
} else {
|
||||
canonicalizedResources += escapeQuery(key) + "=" + escapeQuery(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
method := strings.ToUpper(req.Method)
|
||||
|
||||
dateStr := time.Now().UTC().Format(http.TimeFormat)
|
||||
|
||||
contentMd5 := req.Header.Get("Content-MD5")
|
||||
contentType := req.Header.Get("Content-Type")
|
||||
|
||||
stringToSign := fmt.Sprintf("%s\n%s\n%s\n%s\n%s%s", method, contentMd5, contentType, dateStr, canonicalizedHeaders, canonicalizedResources)
|
||||
|
||||
h := hmac.New(sha1.New, []byte(opts.SecretAccessKey))
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
|
||||
req.Header.Set("Authorization", fmt.Sprintf("OBS %s:%s", opts.AccessKeyId, signature))
|
||||
req.Header.Set("Date", dateStr)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
@ -216,32 +152,3 @@ func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*rest
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func escapeQuery(str string) string {
|
||||
res := url.QueryEscape(str)
|
||||
res = strings.ReplaceAll(res, "%7E", "~")
|
||||
res = strings.ReplaceAll(res, "%2F", "/")
|
||||
res = strings.ReplaceAll(res, "%20", "+")
|
||||
return res
|
||||
}
|
||||
|
||||
func escapePath(path string) string {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(path); i++ {
|
||||
c := path[i]
|
||||
noEscape := (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' ||
|
||||
c == '.' ||
|
||||
c == '_' ||
|
||||
c == '~' ||
|
||||
c == '/'
|
||||
if noEscape {
|
||||
buf.WriteByte(c)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "%%%02X", c)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
123
pkg/sdk3rd/huaweicloud/obs/signer.go
Normal file
123
pkg/sdk3rd/huaweicloud/obs/signer.go
Normal file
@ -0,0 +1,123 @@
|
||||
package obs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
accessKeyId string
|
||||
secretAccessKey string
|
||||
bucket string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://support.huaweicloud.com/api-obs/obs_04_0010.html
|
||||
|
||||
canonicalizedHeaders := ""
|
||||
if len(req.Header) > 0 {
|
||||
keys := make([]string, 0, len(req.Header))
|
||||
for key := range req.Header {
|
||||
key = strings.ToLower(key)
|
||||
if strings.HasPrefix(key, "x-obs-") {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
value := strings.TrimSpace(req.Header.Get(key))
|
||||
canonicalizedHeaders += key + ":" + escapeQuery(value)
|
||||
canonicalizedHeaders += "\n"
|
||||
}
|
||||
}
|
||||
|
||||
bucketName := s.bucket
|
||||
objectName := strings.Trim(req.URL.Path, "/")
|
||||
canonicalizedResources := escapePath(fmt.Sprintf("/%s/%s", bucketName, objectName))
|
||||
if bucketName == "" && objectName == "" {
|
||||
canonicalizedResources = "/"
|
||||
}
|
||||
if len(req.URL.Query()) > 0 {
|
||||
query := req.URL.Query()
|
||||
|
||||
keys := make([]string, 0, len(query))
|
||||
for key := range query {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i == 0 {
|
||||
canonicalizedResources += "?"
|
||||
} else {
|
||||
canonicalizedResources += "&"
|
||||
}
|
||||
|
||||
value := query.Get(key)
|
||||
if value == "" {
|
||||
canonicalizedResources += escapeQuery(key)
|
||||
} else {
|
||||
canonicalizedResources += escapeQuery(key) + "=" + escapeQuery(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
method := strings.ToUpper(req.Method)
|
||||
|
||||
dateStr := time.Now().UTC().Format(http.TimeFormat)
|
||||
|
||||
contentMd5 := req.Header.Get("Content-MD5")
|
||||
contentType := req.Header.Get("Content-Type")
|
||||
|
||||
stringToSign := fmt.Sprintf("%s\n%s\n%s\n%s\n%s%s", method, contentMd5, contentType, dateStr, canonicalizedHeaders, canonicalizedResources)
|
||||
|
||||
h := hmac.New(sha1.New, []byte(s.secretAccessKey))
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
|
||||
authorization := fmt.Sprintf("OBS %s:%s", s.accessKeyId, signature)
|
||||
|
||||
req.Header.Set("Authorization", authorization)
|
||||
req.Header.Set("Date", dateStr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func escapeQuery(str string) string {
|
||||
res := url.QueryEscape(str)
|
||||
res = strings.ReplaceAll(res, "%7E", "~")
|
||||
res = strings.ReplaceAll(res, "%2F", "/")
|
||||
res = strings.ReplaceAll(res, "%20", "+")
|
||||
return res
|
||||
}
|
||||
|
||||
func escapePath(path string) string {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(path); i++ {
|
||||
c := path[i]
|
||||
noEscape := (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' ||
|
||||
c == '.' ||
|
||||
c == '_' ||
|
||||
c == '~' ||
|
||||
c == '/'
|
||||
if noEscape {
|
||||
buf.WriteByte(c)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "%%%02X", c)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
@ -38,14 +38,14 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
baseUrl += fmt.Sprintf("/%s", url.PathEscape(opts.Workspace))
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL(baseUrl).
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetHeader("Kong-Admin-Token", opts.ApiToken)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -51,7 +51,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
client.rc = resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/prod-api").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if client.accessToken != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+client.accessToken)
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if client.accessToken != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+client.accessToken)
|
||||
}
|
||||
|
||||
@ -24,14 +24,14 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset accessToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://api.linode.com/v4").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+opts.AccessToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -43,7 +43,7 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if client.jwtToken != "" {
|
||||
req.Header.Set("JWT", "Bearer "+client.jwtToken)
|
||||
}
|
||||
|
||||
@ -24,14 +24,14 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://api.netlify.com/api/v1").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+opts.ApiToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -51,7 +51,7 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if client.jwtToken != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+client.jwtToken)
|
||||
}
|
||||
|
||||
@ -36,14 +36,14 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset tokenSecret")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/api2/json").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("PVEAPIToken=%s=%s", opts.TokenId, opts.TokenSecret)).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
@ -32,43 +28,24 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset secretAccessKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
accessKeyId: options.AccessKeyId,
|
||||
secretAccessKey: options.SecretAccessKey,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL("http://api.routewize.com").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
// 签名机制:
|
||||
// https://docsv4.qingcloud.com/user_guide/development_docs/api/api_list/dns/calling_method/signature/
|
||||
|
||||
date := time.Now().UTC().Format(time.RFC1123)
|
||||
|
||||
verb := req.Method
|
||||
|
||||
canonicalizedResource := "/"
|
||||
if req.URL != nil {
|
||||
canonicalizedResource = req.URL.Path
|
||||
if req.URL.RawQuery != "" {
|
||||
values, _ := url.ParseQuery(req.URL.RawQuery)
|
||||
canonicalizedResource += "?" + values.Encode()
|
||||
}
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
|
||||
stringToSign := verb + "\n" +
|
||||
date + "\n" +
|
||||
canonicalizedResource
|
||||
|
||||
h := hmac.New(sha256.New, []byte(options.SecretAccessKey))
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
|
||||
req.Header.Set("Date", date)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("QC-HMAC-SHA256 %s:%s", options.AccessKeyId, signature))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
49
pkg/sdk3rd/qingcloud/dns/signer.go
Normal file
49
pkg/sdk3rd/qingcloud/dns/signer.go
Normal file
@ -0,0 +1,49 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
accessKeyId string
|
||||
secretAccessKey string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// 签名机制:
|
||||
// https://docsv4.qingcloud.com/user_guide/development_docs/api/api_list/dns/calling_method/signature/
|
||||
|
||||
date := time.Now().UTC().Format(time.RFC1123)
|
||||
|
||||
verb := req.Method
|
||||
|
||||
canonicalizedResource := "/"
|
||||
if req.URL != nil {
|
||||
canonicalizedResource = req.URL.Path
|
||||
if req.URL.RawQuery != "" {
|
||||
values, _ := url.ParseQuery(req.URL.RawQuery)
|
||||
canonicalizedResource += "?" + values.Encode()
|
||||
}
|
||||
}
|
||||
|
||||
stringToSign := verb + "\n" +
|
||||
date + "\n" +
|
||||
canonicalizedResource
|
||||
|
||||
h := hmac.New(sha256.New, []byte(s.secretAccessKey))
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
|
||||
authorization := fmt.Sprintf("QC-HMAC-SHA256 %s:%s", s.accessKeyId, signature)
|
||||
|
||||
req.Header.Set("Date", date)
|
||||
req.Header.Set("Authorization", authorization)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -24,14 +24,14 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://api.v2.rainyun.com").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetHeader("X-API-Key", options.ApiKey)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -1,14 +1,9 @@
|
||||
package ratpanel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@ -42,58 +37,24 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset accessToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
accessTokenId: options.AccessTokenId,
|
||||
accessToken: options.AccessToken,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/api").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://ratpanel.github.io/advanced/api#authentication-mechanism
|
||||
|
||||
payloadStr := ""
|
||||
if req.Body != nil {
|
||||
payloadb, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payloadStr = string(payloadb)
|
||||
req.Body = io.NopCloser(bytes.NewReader(payloadb))
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
|
||||
canonicalPath := req.URL.Path
|
||||
if !strings.HasPrefix(canonicalPath, "/api") {
|
||||
index := strings.Index(canonicalPath, "/api")
|
||||
if index != -1 {
|
||||
canonicalPath = canonicalPath[index:]
|
||||
}
|
||||
}
|
||||
|
||||
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s",
|
||||
req.Method,
|
||||
canonicalPath,
|
||||
req.URL.Query().Encode(),
|
||||
sumSha256(payloadStr),
|
||||
)
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
stringToSign := fmt.Sprintf("%s\n%d\n%s",
|
||||
"HMAC-SHA256",
|
||||
timestamp,
|
||||
sumSha256(canonicalRequest),
|
||||
)
|
||||
|
||||
signature := sumHmacSha256(stringToSign, options.AccessToken)
|
||||
|
||||
req.Header.Set("X-Timestamp", fmt.Sprintf("%d", timestamp))
|
||||
req.Header.Set("Authorization", fmt.Sprintf("HMAC-SHA256 Credential=%d, Signature=%s", options.AccessTokenId, signature))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
@ -163,16 +124,3 @@ func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*rest
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func sumSha256(str string) string {
|
||||
sum := sha256.Sum256([]byte(str))
|
||||
dst := make([]byte, hex.EncodedLen(len(sum)))
|
||||
hex.Encode(dst, sum[:])
|
||||
return string(dst)
|
||||
}
|
||||
|
||||
func sumHmacSha256(data string, secret string) string {
|
||||
h := hmac.New(sha256.New, []byte(secret))
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
75
pkg/sdk3rd/ratpanel/signer.go
Normal file
75
pkg/sdk3rd/ratpanel/signer.go
Normal file
@ -0,0 +1,75 @@
|
||||
package ratpanel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
accessTokenId int64
|
||||
accessToken string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://ratpanel.github.io/advanced/api#authentication-mechanism
|
||||
|
||||
payloadStr := ""
|
||||
if req.Body != nil {
|
||||
payloadb, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payloadStr = string(payloadb)
|
||||
req.Body = io.NopCloser(bytes.NewReader(payloadb))
|
||||
}
|
||||
|
||||
canonicalPath := req.URL.Path
|
||||
if !strings.HasPrefix(canonicalPath, "/api") {
|
||||
index := strings.Index(canonicalPath, "/api")
|
||||
if index != -1 {
|
||||
canonicalPath = canonicalPath[index:]
|
||||
}
|
||||
}
|
||||
|
||||
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s",
|
||||
req.Method,
|
||||
canonicalPath,
|
||||
req.URL.Query().Encode(),
|
||||
sumSha256(payloadStr),
|
||||
)
|
||||
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
|
||||
stringToSign := fmt.Sprintf("%s\n%s\n%s", "HMAC-SHA256", timestamp, sumSha256(canonicalRequest))
|
||||
|
||||
signature := sumHmacSha256(stringToSign, s.accessToken)
|
||||
|
||||
authorization := fmt.Sprintf("HMAC-SHA256 Credential=%d, Signature=%s", s.accessTokenId, signature)
|
||||
|
||||
req.Header.Set("X-Timestamp", timestamp)
|
||||
req.Header.Set("Authorization", authorization)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sumSha256(str string) string {
|
||||
sum := sha256.Sum256([]byte(str))
|
||||
dst := make([]byte, hex.EncodedLen(len(sum)))
|
||||
hex.Encode(dst, sum[:])
|
||||
return string(dst)
|
||||
}
|
||||
|
||||
func sumHmacSha256(data string, secret string) string {
|
||||
h := hmac.New(sha256.New, []byte(secret))
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
@ -33,14 +33,14 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")).
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetHeader("X-SLCE-API-TOKEN", options.ApiToken)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -33,14 +33,14 @@ func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimRight(serverUrl, "/")+"/api/v1").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetHeader("X-API-Key", options.ApiKey)
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -38,7 +38,7 @@ func NewClient(serverUrl string) (*Client, error) {
|
||||
client.rc = resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")).
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if client.synoToken != "" {
|
||||
req.Header.Set("X-SYNO-TOKEN", client.synoToken)
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if client.loginCookie != "" {
|
||||
req.Header.Set("Cookie", client.loginCookie)
|
||||
}
|
||||
|
||||
@ -25,13 +25,13 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset apiToken")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://api.vercel.com/v8").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+options.ApiToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if options.TeamId != "" {
|
||||
qs := req.URL.Query()
|
||||
qs.Set("teamId", options.TeamId)
|
||||
@ -41,7 +41,7 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
@ -1,19 +1,12 @@
|
||||
package tos
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
@ -55,121 +48,23 @@ func NewClient(endpoint string, optFns ...OptionsFunc) (*Client, error) {
|
||||
}
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
accessKeyId: opts.AccessKeyId,
|
||||
secretAccessKey: opts.SecretAccessKey,
|
||||
region: opts.Region,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL(endpoint).
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://www.volcengine.com/docs/6349/74839
|
||||
// https://docs.byteplus.com/en/docs/tos/reference-signature-mechanism_1
|
||||
|
||||
method := strings.ToUpper(req.Method)
|
||||
|
||||
nowUtc := time.Now().UTC()
|
||||
headerDateStr := nowUtc.Format(http.TimeFormat)
|
||||
requestDateStr := nowUtc.Format("20060102T150405Z")
|
||||
credentialDateStr := nowUtc.Format("20060102")
|
||||
|
||||
canonicalUrl := escapePath(req.URL.Path)
|
||||
if canonicalUrl == "" {
|
||||
canonicalUrl = "/"
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
|
||||
canonicalQueryStr := ""
|
||||
if len(req.URL.Query()) > 0 {
|
||||
query := req.URL.Query()
|
||||
|
||||
keys := make([]string, 0, len(query))
|
||||
for key := range query {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
canonicalQueryStr += "&"
|
||||
}
|
||||
|
||||
value := query.Get(key)
|
||||
canonicalQueryStr += escapeQuery(key) + "=" + escapeQuery(value)
|
||||
}
|
||||
}
|
||||
|
||||
canonicalHeaders := ""
|
||||
signedHeaders := ""
|
||||
if len(req.Header) > 0 || req.Host != "" {
|
||||
if req.Header.Get("Host") == "" {
|
||||
req.Header.Set("Host", req.Host)
|
||||
}
|
||||
if req.Header.Get("X-TOS-Date") == "" {
|
||||
req.Header.Set("X-TOS-Date", requestDateStr)
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(req.Header))
|
||||
for key := range req.Header {
|
||||
key = strings.ToLower(key)
|
||||
if strings.HasPrefix(key, "x-tos-") || key == "host" {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
if key == "content-type" && req.Header.Get("X-TOS-Content-SHA256") != "" {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
canonicalHeaders += "\n"
|
||||
signedHeaders += ";"
|
||||
}
|
||||
|
||||
value := strings.TrimSpace(req.Header.Get(key))
|
||||
canonicalHeaders += key + ":" + value
|
||||
signedHeaders += key
|
||||
}
|
||||
|
||||
canonicalHeaders += "\n"
|
||||
}
|
||||
|
||||
hashedPayload := req.Header.Get("X-TOS-Content-SHA256")
|
||||
if hashedPayload == "" {
|
||||
temp := sha256.Sum256([]byte{})
|
||||
hashedPayload = strings.ToLower(hex.EncodeToString(temp[:]))
|
||||
}
|
||||
|
||||
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", method, canonicalUrl, canonicalQueryStr, canonicalHeaders, signedHeaders, hashedPayload)
|
||||
canonicalRequestHash := sha256.Sum256([]byte(canonicalRequest))
|
||||
canonicalRequestHashHex := strings.ToLower(hex.EncodeToString(canonicalRequestHash[:]))
|
||||
|
||||
const signAlgorithmHeader = "TOS4-HMAC-SHA256"
|
||||
credentialScope := fmt.Sprintf("%s/%s/tos/request", credentialDateStr, opts.Region)
|
||||
stringToSign := fmt.Sprintf("%s\n%s\n%s\n%s", signAlgorithmHeader, requestDateStr, credentialScope, canonicalRequestHashHex)
|
||||
|
||||
var h hash.Hash
|
||||
h = hmac.New(sha256.New, []byte(opts.SecretAccessKey))
|
||||
h.Write([]byte(credentialDateStr))
|
||||
kDate := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kDate)
|
||||
h.Write([]byte(opts.Region))
|
||||
kRegion := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kRegion)
|
||||
h.Write([]byte("tos"))
|
||||
kService := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kService)
|
||||
h.Write([]byte("request"))
|
||||
kSigning := h.Sum(nil)
|
||||
|
||||
h = hmac.New(sha256.New, kSigning)
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
|
||||
|
||||
req.Header.Set("Authorization", fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s", signAlgorithmHeader, opts.AccessKeyId, credentialScope, signedHeaders, signature))
|
||||
req.Header.Set("Date", headerDateStr)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
@ -257,30 +152,3 @@ func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*rest
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func escapeQuery(str string) string {
|
||||
res := url.QueryEscape(str)
|
||||
res = strings.ReplaceAll(res, "+", "%20")
|
||||
return res
|
||||
}
|
||||
|
||||
func escapePath(path string) string {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(path); i++ {
|
||||
c := path[i]
|
||||
noEscape := (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' ||
|
||||
c == '.' ||
|
||||
c == '_' ||
|
||||
c == '~' ||
|
||||
c == '/'
|
||||
if noEscape {
|
||||
buf.WriteByte(c)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "%%%02X", c)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
161
pkg/sdk3rd/volcengine/tos/signer.go
Normal file
161
pkg/sdk3rd/volcengine/tos/signer.go
Normal file
@ -0,0 +1,161 @@
|
||||
package tos
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
accessKeyId string
|
||||
secretAccessKey string
|
||||
region string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://www.volcengine.com/docs/6349/74839
|
||||
// https://docs.byteplus.com/en/docs/tos/reference-signature-mechanism_1
|
||||
|
||||
method := strings.ToUpper(req.Method)
|
||||
|
||||
nowUtc := time.Now().UTC()
|
||||
headerDateStr := nowUtc.Format(http.TimeFormat)
|
||||
requestDateStr := nowUtc.Format("20060102T150405Z")
|
||||
credentialDateStr := nowUtc.Format("20060102")
|
||||
|
||||
canonicalUrl := escapePath(req.URL.Path)
|
||||
if canonicalUrl == "" {
|
||||
canonicalUrl = "/"
|
||||
}
|
||||
|
||||
canonicalQueryStr := ""
|
||||
if len(req.URL.Query()) > 0 {
|
||||
query := req.URL.Query()
|
||||
|
||||
keys := make([]string, 0, len(query))
|
||||
for key := range query {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
canonicalQueryStr += "&"
|
||||
}
|
||||
|
||||
value := query.Get(key)
|
||||
canonicalQueryStr += escapeQuery(key) + "=" + escapeQuery(value)
|
||||
}
|
||||
}
|
||||
|
||||
canonicalHeaders := ""
|
||||
signedHeaders := ""
|
||||
if len(req.Header) > 0 || req.Host != "" {
|
||||
if req.Header.Get("Host") == "" {
|
||||
req.Header.Set("Host", req.Host)
|
||||
}
|
||||
if req.Header.Get("X-TOS-Date") == "" {
|
||||
req.Header.Set("X-TOS-Date", requestDateStr)
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(req.Header))
|
||||
for key := range req.Header {
|
||||
key = strings.ToLower(key)
|
||||
if strings.HasPrefix(key, "x-tos-") || key == "host" {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
if key == "content-type" && req.Header.Get("X-TOS-Content-SHA256") != "" {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
canonicalHeaders += "\n"
|
||||
signedHeaders += ";"
|
||||
}
|
||||
|
||||
value := strings.TrimSpace(req.Header.Get(key))
|
||||
canonicalHeaders += key + ":" + value
|
||||
signedHeaders += key
|
||||
}
|
||||
|
||||
canonicalHeaders += "\n"
|
||||
}
|
||||
|
||||
hashedPayload := req.Header.Get("X-TOS-Content-SHA256")
|
||||
if hashedPayload == "" {
|
||||
temp := sha256.Sum256([]byte{})
|
||||
hashedPayload = strings.ToLower(hex.EncodeToString(temp[:]))
|
||||
}
|
||||
|
||||
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", method, canonicalUrl, canonicalQueryStr, canonicalHeaders, signedHeaders, hashedPayload)
|
||||
canonicalRequestHash := sha256.Sum256([]byte(canonicalRequest))
|
||||
canonicalRequestHashHex := strings.ToLower(hex.EncodeToString(canonicalRequestHash[:]))
|
||||
|
||||
const signAlgorithmHeader = "TOS4-HMAC-SHA256"
|
||||
credentialScope := fmt.Sprintf("%s/%s/tos/request", credentialDateStr, s.region)
|
||||
stringToSign := fmt.Sprintf("%s\n%s\n%s\n%s", signAlgorithmHeader, requestDateStr, credentialScope, canonicalRequestHashHex)
|
||||
|
||||
var h hash.Hash
|
||||
h = hmac.New(sha256.New, []byte(s.secretAccessKey))
|
||||
h.Write([]byte(credentialDateStr))
|
||||
kDate := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kDate)
|
||||
h.Write([]byte(s.region))
|
||||
kRegion := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kRegion)
|
||||
h.Write([]byte("tos"))
|
||||
kService := h.Sum(nil)
|
||||
h = hmac.New(sha256.New, kService)
|
||||
h.Write([]byte("request"))
|
||||
kSigning := h.Sum(nil)
|
||||
|
||||
h = hmac.New(sha256.New, kSigning)
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
|
||||
|
||||
authorization := fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s", signAlgorithmHeader, s.accessKeyId, credentialScope, signedHeaders, signature)
|
||||
|
||||
req.Header.Set("Authorization", authorization)
|
||||
req.Header.Set("Date", headerDateStr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func escapeQuery(str string) string {
|
||||
res := url.QueryEscape(str)
|
||||
res = strings.ReplaceAll(res, "+", "%20")
|
||||
return res
|
||||
}
|
||||
|
||||
func escapePath(path string) string {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(path); i++ {
|
||||
c := path[i]
|
||||
noEscape := (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' ||
|
||||
c == '.' ||
|
||||
c == '_' ||
|
||||
c == '~' ||
|
||||
c == '/'
|
||||
if noEscape {
|
||||
buf.WriteByte(c)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, "%%%02X", c)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
@ -1,17 +1,9 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
@ -36,85 +28,24 @@ func NewClient(optFns ...OptionsFunc) (*Client, error) {
|
||||
return nil, fmt.Errorf("sdkerr: unset secretKey")
|
||||
}
|
||||
|
||||
restyClient := resty.New().
|
||||
signer := &signer{
|
||||
accessKey: options.AccessKey,
|
||||
secretKey: options.SecretKey,
|
||||
}
|
||||
httper := resty.New().
|
||||
SetBaseURL("https://open.chinanetcenter.com").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://www.wangsu.com/document/openapi/api-authentication
|
||||
|
||||
method := strings.ToUpper(req.Method)
|
||||
|
||||
path := "/"
|
||||
if req.URL != nil {
|
||||
path = req.URL.Path
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if err := signer.Sign(req); err != nil {
|
||||
return fmt.Errorf("sdkerr: sign error: %w", err)
|
||||
}
|
||||
|
||||
queryStr := ""
|
||||
if method != http.MethodPost && req.URL != nil {
|
||||
queryStr = req.URL.RawQuery
|
||||
|
||||
s, err := url.QueryUnescape(queryStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
queryStr = s
|
||||
}
|
||||
|
||||
canonicalHeaders := "" +
|
||||
"content-type:" + strings.TrimSpace(strings.ToLower(req.Header.Get("Content-Type"))) + "\n" +
|
||||
"host:" + strings.TrimSpace(strings.ToLower(req.Host)) + "\n"
|
||||
signedHeaders := "content-type;host"
|
||||
|
||||
payloadStr := ""
|
||||
if method != http.MethodGet && req.Body != nil {
|
||||
payloadb, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payloadStr = string(payloadb)
|
||||
req.Body = io.NopCloser(bytes.NewReader(payloadb))
|
||||
}
|
||||
payloadHash := sha256.Sum256([]byte(payloadStr))
|
||||
payloadHashHex := strings.ToLower(hex.EncodeToString(payloadHash[:]))
|
||||
|
||||
nowUtc := time.Now().UTC()
|
||||
timestampStr := req.Header.Get("X-CNC-Timestamp")
|
||||
if timestampStr == "" {
|
||||
timestampStr = fmt.Sprintf("%d", nowUtc.Unix())
|
||||
} else {
|
||||
timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nowUtc = time.Unix(timestamp, 0).UTC()
|
||||
}
|
||||
|
||||
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", method, path, queryStr, canonicalHeaders, signedHeaders, payloadHashHex)
|
||||
canonicalRequestHash := sha256.Sum256([]byte(canonicalRequest))
|
||||
canonicalRequestHashHex := strings.ToLower(hex.EncodeToString(canonicalRequestHash[:]))
|
||||
|
||||
const signAlgorithmHeader = "CNC-HMAC-SHA256"
|
||||
stringToSign := fmt.Sprintf("%s\n%s\n%s", signAlgorithmHeader, timestampStr, canonicalRequestHashHex)
|
||||
|
||||
h := hmac.New(sha256.New, []byte(options.SecretKey))
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
|
||||
|
||||
req.Header.Set("Authorization", fmt.Sprintf("%s Credential=%s, SignedHeaders=%s, Signature=%s", signAlgorithmHeader, options.AccessKey, signedHeaders, signature))
|
||||
req.Header.Set("Date", nowUtc.Format(http.TimeFormat))
|
||||
req.Header.Set("X-CNC-Auth-Method", "AKSK")
|
||||
req.Header.Set("X-CNC-AccessKey", options.AccessKey)
|
||||
req.Header.Set("X-CNC-Timestamp", timestampStr)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{rc: restyClient}, nil
|
||||
return &Client{rc: httper}, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
|
||||
95
pkg/sdk3rd/wangsu/openapi/signer.go
Normal file
95
pkg/sdk3rd/wangsu/openapi/signer.go
Normal file
@ -0,0 +1,95 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type signer struct {
|
||||
accessKey string
|
||||
secretKey string
|
||||
}
|
||||
|
||||
func (s *signer) Sign(req *http.Request) error {
|
||||
// API 签名机制:
|
||||
// https://www.wangsu.com/document/openapi/api-authentication
|
||||
|
||||
method := strings.ToUpper(req.Method)
|
||||
|
||||
path := "/"
|
||||
if req.URL != nil {
|
||||
path = req.URL.Path
|
||||
}
|
||||
|
||||
queryStr := ""
|
||||
if method != http.MethodPost && req.URL != nil {
|
||||
queryStr = req.URL.RawQuery
|
||||
|
||||
s, err := url.QueryUnescape(queryStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
queryStr = s
|
||||
}
|
||||
|
||||
canonicalHeaders := "" +
|
||||
"content-type:" + strings.TrimSpace(strings.ToLower(req.Header.Get("Content-Type"))) + "\n" +
|
||||
"host:" + strings.TrimSpace(strings.ToLower(req.Host)) + "\n"
|
||||
signedHeaders := "content-type;host"
|
||||
|
||||
payloadStr := ""
|
||||
if method != http.MethodGet && req.Body != nil {
|
||||
payloadb, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payloadStr = string(payloadb)
|
||||
req.Body = io.NopCloser(bytes.NewReader(payloadb))
|
||||
}
|
||||
payloadHash := sha256.Sum256([]byte(payloadStr))
|
||||
payloadHashHex := strings.ToLower(hex.EncodeToString(payloadHash[:]))
|
||||
|
||||
nowUtc := time.Now().UTC()
|
||||
timestampStr := req.Header.Get("X-CNC-Timestamp")
|
||||
if timestampStr == "" {
|
||||
timestampStr = fmt.Sprintf("%d", nowUtc.Unix())
|
||||
} else {
|
||||
timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nowUtc = time.Unix(timestamp, 0).UTC()
|
||||
}
|
||||
|
||||
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", method, path, queryStr, canonicalHeaders, signedHeaders, payloadHashHex)
|
||||
canonicalRequestHash := sha256.Sum256([]byte(canonicalRequest))
|
||||
canonicalRequestHashHex := strings.ToLower(hex.EncodeToString(canonicalRequestHash[:]))
|
||||
|
||||
const signAlgorithmHeader = "CNC-HMAC-SHA256"
|
||||
stringToSign := fmt.Sprintf("%s\n%s\n%s", signAlgorithmHeader, timestampStr, canonicalRequestHashHex)
|
||||
|
||||
h := hmac.New(sha256.New, []byte(s.secretKey))
|
||||
h.Write([]byte(stringToSign))
|
||||
signature := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
|
||||
|
||||
authorization := fmt.Sprintf("%s Credential=%s, SignedHeaders=%s, Signature=%s", signAlgorithmHeader, s.accessKey, signedHeaders, signature)
|
||||
|
||||
req.Header.Set("Authorization", authorization)
|
||||
req.Header.Set("Date", nowUtc.Format(http.TimeFormat))
|
||||
req.Header.Set("X-CNC-Auth-Method", "AKSK")
|
||||
req.Header.Set("X-CNC-AccessKey", s.accessKey)
|
||||
req.Header.Set("X-CNC-Timestamp", timestampStr)
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user