diff --git a/pkg/filesystem/driver/cos/handler.go b/pkg/filesystem/driver/cos/handler.go index 50b500c5..63e5c4a4 100644 --- a/pkg/filesystem/driver/cos/handler.go +++ b/pkg/filesystem/driver/cos/handler.go @@ -3,14 +3,18 @@ package cos import ( "context" "crypto/hmac" + "crypto/md5" "crypto/sha1" "encoding/base64" + "encoding/hex" "encoding/json" "errors" "fmt" + "github.com/google/uuid" "io" "net/http" "net/url" + "os" "path" "path/filepath" "strings" @@ -311,7 +315,7 @@ func (handler Driver) signSourceURL(ctx context.Context, path string, ttl int64, file.RawQuery = optionQuery.Encode() sourceURL := cdnURL.ResolveReference(file) - return sourceURL.String(), nil + return signCDNURL(sourceURL.String()) } presignedURL, err := handler.Client.Object.GetPresignedURL(ctx, http.MethodGet, path, @@ -327,6 +331,48 @@ func (handler Driver) signSourceURL(ctx context.Context, path string, ttl int64, return presignedURL.String(), nil } +// 支持腾讯云 CDN 的 Type A 鉴权 +func signCDNURL(rawUrl string) (string, error) { + // 初始化参数 + cdnHostname := os.Getenv("TCLOUD_CDN_HOSTNAME") + cdnSignKey := os.Getenv("TCLOUD_CDN_SIGN_KEY") + cdnSignKeyURLParam := os.Getenv("TCLOUD_CDN_KEY_URL_PARAM") + if cdnHostname == "" || cdnSignKey == "" || cdnSignKeyURLParam == "" { + return rawUrl, nil + } + + // 解析 URL + parsedUrl, err := url.Parse(rawUrl) + if err != nil { + return "", err + } + + // 判断是否需要签名 + if parsedUrl.Hostname() != cdnHostname { + return rawUrl, nil + } + + // 签名 + timestamp := time.Now().Unix() + random, err := uuid.NewRandom() + if err != nil { + return "", err + } + nonce := strings.Replace(random.String(), "-", "", 4) + uid := "0" + signatureString := fmt.Sprintf("%s-%d-%s-%s-%s", parsedUrl.Path, timestamp, nonce, uid, cdnSignKey) + hash := md5.Sum([]byte(signatureString)) + signature := hex.EncodeToString(hash[:]) + + // 添加签名到URL参数 + urlQuery := parsedUrl.Query() + urlQuery.Set(cdnSignKeyURLParam, fmt.Sprintf("%d-%s-%s-%s", timestamp, nonce, uid, signature)) + parsedUrl.RawQuery = urlQuery.Encode() + + // 返回URL + return parsedUrl.String(), nil +} + // Token 获取上传策略和认证Token func (handler Driver) Token(ctx context.Context, ttl int64, uploadSession *serializer.UploadSession, file fsctx.FileHeader) (*serializer.UploadCredential, error) { // 生成回调地址