Skip to content

Commit

Permalink
update: [Tencent] CAM & TC3 signature authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
kainonly committed Oct 19, 2023
1 parent 382f9c7 commit 698addf
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 45 deletions.
2 changes: 1 addition & 1 deletion api/tencent/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ var Provides = wire.NewSet(
wire.Struct(new(Service), "*"),
)

type M map[string]interface{}
type M = map[string]interface{}
165 changes: 121 additions & 44 deletions api/tencent/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import (
"github.com/weplanx/server/common"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"time"
)

Expand Down Expand Up @@ -86,6 +89,95 @@ func (x *Service) CosImageInfo(ctx context.Context, url string) (r M, err error)
return
}

type TC3Option struct {
Service string
Headers map[string]string
Timestamp int64
Body interface{}
}

func (x *Service) TC3Authorization(option TC3Option) string {
algorithm := "TC3-HMAC-SHA256"
canonicalURI := "/"
canonicalQueryString := ""

var keys []string
for key := range option.Headers {
keys = append(keys, key)
}
sort.Strings(keys)

var canonicalHeaders string
var signedHeaders string
for _, key := range keys {
k, v := strings.ToLower(key), strings.ToLower(option.Headers[key])
canonicalHeaders += fmt.Sprintf("%s:%s\n", k, v)
signedHeaders += ";" + k
}

signedHeaders = signedHeaders[1:]

payload, _ := sonic.MarshalString(option.Body)
hashedRequestPayload := common.Sha256hex(payload)
canonicalRequest := fmt.Sprintf("POST\n%s\n%s\n%s\n%s\n%s",
canonicalURI,
canonicalQueryString,
canonicalHeaders,
signedHeaders,
hashedRequestPayload,
)

date := time.Unix(option.Timestamp, 0).UTC().Format("2006-01-02")
credentialScope := fmt.Sprintf("%s/%s/tc3_request", date, option.Service)
hashedCanonicalRequest := common.Sha256hex(canonicalRequest)
string2sign := fmt.Sprintf("%s\n%d\n%s\n%s",
algorithm,
option.Timestamp,
credentialScope,
hashedCanonicalRequest,
)

secretDate := common.Hmacsha256(date, "TC3"+x.V.TencentSecretKey)
secretService := common.Hmacsha256(option.Service, secretDate)
secretSigning := common.Hmacsha256("tc3_request", secretService)
signature := hex.EncodeToString([]byte(common.Hmacsha256(string2sign, secretSigning)))

return fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s",
algorithm,
x.V.TencentSecretId,
credentialScope,
signedHeaders,
signature,
)
}

func (x *Service) InvokeAccelerate(ctx context.Context) (r M, err error) {
u, _ := url.Parse(x.V.AccelerateAddress)
timestamp := time.Now().Unix()
headerx := map[string]string{
"Content-Type": "application/json",
"Host": u.Host,
"X-Scf-Cam-Uin": x.V.CamUin,
}
body := M{}
if _, err = common.HttpClient(u.String()).R().SetContext(ctx).
SetHeaders(headerx).
SetHeader("X-Scf-Cam-Timestamp", strconv.FormatInt(timestamp, 10)).
SetHeader("Authorization", x.TC3Authorization(TC3Option{
Service: "scf",
Headers: headerx,
Timestamp: timestamp,
Body: body,
})).
SetBody(body).
SetSuccessResult(&r).
Post(""); err != nil {
return
}

return
}

type KeyAuthResult struct {
Date string
Txt string
Expand Down Expand Up @@ -158,31 +250,22 @@ func (x *Service) GetIpv4(ctx context.Context, ip string) (_ IpResult, err error
if kar, err = x.KeyAuth(source, x.V.IpSecretId, x.V.IpSecretKey); err != nil {
return
}

baseUrl, _ := url.Parse(x.V.IpAddress)
u := baseUrl.JoinPath("/ip/city/query")
query := u.Query()
query.Add("ip", ip)
query.Encode()
u.RawQuery = query.Encode()

var req *http.Request
req, err = http.NewRequest("GET", u.String(), nil)
req.Header.Set("X-Source", source)
req.Header.Set("X-Date", kar.Date)
req.Header.Set("Authorization", kar.Txt)
req.WithContext(ctx)

client := &http.Client{Timeout: time.Second * 5}
var res *http.Response
if res, err = client.Do(req); err != nil {
return
}
var r *Ipv4Result
if err = decoder.NewStreamDecoder(res.Body).Decode(&r); err != nil {
return
var msg string
if _, err = common.HttpClient(x.V.IpAddress).R().
SetContext(ctx).
SetHeader("X-Source", source).
SetHeader("X-Date", kar.Date).
SetHeader("Authorization", kar.Txt).
SetQueryParam("ip", ip).
SetSuccessResult(&r).
SetErrorResult(&msg).
Get("/ip/city/query"); err != nil {
return nil, help.E("Tencent.GetIpv4Fail", err.Error())
}
if msg != "" {
return nil, help.E("Tencent.GetIpv4Fail", msg)
}

return r, nil
}

Expand Down Expand Up @@ -233,29 +316,23 @@ func (x *Service) GetIpv6(ctx context.Context, ip string) (_ IpResult, err error
return
}

baseUrl, _ := url.Parse(x.V.Ipv6Address)
u := baseUrl.JoinPath("/ip/ipv6/query")
query := u.Query()
query.Add("ip", ip)
query.Encode()
u.RawQuery = query.Encode()

var req *http.Request
req, err = http.NewRequest("GET", u.String(), nil)
req.Header.Set("X-Source", source)
req.Header.Set("X-Date", kar.Date)
req.Header.Set("Authorization", kar.Txt)
req.WithContext(ctx)

client := &http.Client{Timeout: time.Second * 5}
var res *http.Response
if res, err = client.Do(req); err != nil {
return
}
var r *Ipv6Result
if err = decoder.NewStreamDecoder(res.Body).Decode(&r); err != nil {
return
var msg string
if _, err = common.HttpClient(x.V.Ipv6Address).R().
SetContext(ctx).
SetHeader("X-Source", source).
SetHeader("X-Date", kar.Date).
SetHeader("Authorization", kar.Txt).
SetQueryParam("ip", ip).
SetSuccessResult(&r).
SetErrorResult(&msg).
Get("/ip/ipv6/query"); err != nil {
return nil, help.E("Tencent.GetIpv6Fail", err.Error())
}
if msg != "" {
return nil, help.E("Tencent.GetIpv6Fail", msg)
}

return r, nil
}

Expand Down
14 changes: 14 additions & 0 deletions api/tencent/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,25 @@ import (
"testing"
)

func TestService_InvokeAccelerate(t *testing.T) {
ctx := context.TODO()
r, err := x.TencentService.InvokeAccelerate(ctx)
assert.NoError(t, err)
t.Log(r)
}

func TestService_GetIpv4(t *testing.T) {
ctx := context.TODO()
dto, err := x.TencentService.GetIpv4(ctx, "119.41.34.152")
assert.NoError(t, err)
t.Log(dto)

}

func TestService_GetIpv4BadResp(t *testing.T) {
ctx := context.TODO()
_, err := x.TencentService.GetIpv4(ctx, "xa.41.34.152")
assert.Error(t, err)
}

func TestService_GetIpv6(t *testing.T) {
Expand Down
25 changes: 25 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package common

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"github.com/bytedance/sonic"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/protocol"
"github.com/imroc/req/v3"
"github.com/nats-io/nats.go"
"github.com/redis/go-redis/v9"
transfer "github.com/weplanx/collector/client"
Expand All @@ -11,6 +16,7 @@ import (
"github.com/weplanx/go/locker"
"github.com/weplanx/go/passport"
"go.mongodb.org/mongo-driver/mongo"
"time"
)

type Inject struct {
Expand Down Expand Up @@ -46,3 +52,22 @@ func ClearAccessToken(c *app.RequestContext) {
c.SetCookie("TOKEN", "", -1,
"/", "", protocol.CookieSameSiteLaxMode, true, true)
}

func HttpClient(url string) *req.Client {
return req.C().
SetBaseURL(url).
SetJsonMarshal(sonic.Marshal).
SetJsonUnmarshal(sonic.Unmarshal).
SetTimeout(time.Second * 5)
}

func Sha256hex(s string) string {
b := sha256.Sum256([]byte(s))
return hex.EncodeToString(b[:])
}

func Hmacsha256(s, key string) string {
hashed := hmac.New(sha256.New, []byte(key))
hashed.Write([]byte(s))
return string(hashed.Sum(nil))
}
2 changes: 2 additions & 0 deletions common/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type Extra struct {
EmqxHost string `yaml:"emqx_host"`
EmqxApiKey string `yaml:"emqx_api_key"`
EmqxSecretKey string `yaml:"emqx_secret_key" secret:"*"`
AccelerateAddress string `yaml:"accelerate_address"`
CamUin string `yaml:"cam_uin"`
*values.DynamicValues `yaml:"dynamic_values"`
}

Expand Down

0 comments on commit 698addf

Please sign in to comment.