低空智控平台 后端go
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

101 lines
2.6 KiB

package common
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"net/url"
"strconv"
"strings"
"time"
)
// PresignOSS 生成 S3 兼容的预签名 URL(MinIO / 阿里云 OSS),method 为 HTTP 方法(GET/PUT)
// objectKey 为对象 key(不含 bucket),expires 为有效期秒数。
func PresignOSS(method, objectKey string, expires int64) (string, error) {
oss := AppConf.OSS
if oss.Endpoint == "" {
return "", NewBusiError(InternalError, "OSS 未配置")
}
u, err := url.Parse(oss.Endpoint)
if err != nil {
return "", err
}
host := u.Host
scheme := u.Scheme
if scheme == "" {
scheme = "https"
}
region := oss.Region
if region == "" {
region = "us-east-1"
}
const service = "s3"
now := time.Now().UTC()
amzDate := now.Format("20060102T150405Z")
dateStamp := now.Format("20060102")
credentialScope := dateStamp + "/" + region + "/" + service + "/aws4_request"
canonicalURI := encodeObjectPath(oss.Bucket + "/" + objectKey)
query := url.Values{}
query.Set("X-Amz-Algorithm", "AWS4-HMAC-SHA256")
query.Set("X-Amz-Credential", oss.AccessKey+"/"+credentialScope)
query.Set("X-Amz-Date", amzDate)
query.Set("X-Amz-Expires", strconv.FormatInt(expires, 10))
query.Set("X-Amz-SignedHeaders", "host")
canonicalHeaders := "host:" + host + "\n"
const signedHeaders = "host"
const payloadHash = "UNSIGNED-PAYLOAD"
canonicalRequest := strings.Join([]string{
method,
canonicalURI,
query.Encode(),
canonicalHeaders,
signedHeaders,
payloadHash,
}, "\n")
stringToSign := strings.Join([]string{
"AWS4-HMAC-SHA256",
amzDate,
credentialScope,
sha256Hex([]byte(canonicalRequest)),
}, "\n")
signingKey := deriveSigningKey(oss.SecretKey, dateStamp, region, service)
signature := hex.EncodeToString(hmacSHA256(signingKey, []byte(stringToSign)))
query.Set("X-Amz-Signature", signature)
return scheme + "://" + host + canonicalURI + "?" + query.Encode(), nil
}
// encodeObjectPath 对对象路径做分段 URL 编码(保留 "/" 分隔符),并以 "/" 开头
func encodeObjectPath(key string) string {
segs := strings.Split(key, "/")
for i, s := range segs {
segs[i] = url.PathEscape(s)
}
return "/" + strings.Join(segs, "/")
}
func sha256Hex(data []byte) string {
sum := sha256.Sum256(data)
return hex.EncodeToString(sum[:])
}
func hmacSHA256(key, data []byte) []byte {
h := hmac.New(sha256.New, key)
h.Write(data)
return h.Sum(nil)
}
func deriveSigningKey(secret, date, region, service string) []byte {
kDate := hmacSHA256([]byte("AWS4"+secret), []byte(date))
kRegion := hmacSHA256(kDate, []byte(region))
kService := hmacSHA256(kRegion, []byte(service))
return hmacSHA256(kService, []byte("aws4_request"))
}