* Update Request Header Prefixes and Upgrade API Version to 0.6.0 - Changed all HTTP request header prefixes from `x-sls-` to `x-log-` to comply with the latest API specifications. - Adjusted header prefix validation in `signature.go` to support `x-log-` and `x-acs-`. - Replaced `x-sls-bodyrawsize` with `x-log-bodyrawsize` in multiple files (`log_project.go`, `request.go`, etc.). - Updated header fields for compression type and signing method (e.g., `x-sls-compresstype` to `x-log-compresstype`). - Modified authorization logic to replace `SLS` with `LOG`. * Refactor signature generation logic for improved readability and maintainability Split the signature generation logic into several independent functions, including header extraction, standardized header construction, and resource string generation. This modular design enhances code clarity, reduces redundant logic, and improves error handling mechanisms. Additionally, comments and code structure have been optimized to ensure easier extension and debugging. --------- Co-authored-by: 优胜 <zhushaofei.zsf@alibaba-inc.com>
153 lines
3.7 KiB
Go
Executable File
153 lines
3.7 KiB
Go
Executable File
package alils
|
||
|
||
import (
|
||
"crypto/hmac"
|
||
"crypto/sha1"
|
||
"encoding/base64"
|
||
"fmt"
|
||
"net/url"
|
||
"sort"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// GMT location
|
||
var gmtLoc = time.FixedZone("GMT", 0)
|
||
|
||
// NowRFC1123 returns now time in RFC1123 format with GMT timezone,
|
||
// eg. "Mon, 02 Jan 2006 15:04:05 GMT".
|
||
func nowRFC1123() string {
|
||
return time.Now().In(gmtLoc).Format(time.RFC1123)
|
||
}
|
||
|
||
// signature calculates a request's signature digest
|
||
func signature(project *LogProject, method, uri string,
|
||
headers map[string]string) (digest string, err error) {
|
||
|
||
// 1. Extract basic header information
|
||
contentMD5 := getHeaderSafe(headers, "Content-MD5")
|
||
contentType := getHeaderSafe(headers, "Content-Type")
|
||
|
||
date, ok := headers["Date"]
|
||
if !ok {
|
||
return "", fmt.Errorf("Can't find 'Date' header")
|
||
}
|
||
|
||
// 2. Calculate CanonicalizedSLSHeaders
|
||
canoHeaders := buildCanonicalHeaders(headers)
|
||
|
||
// 3. Calculate CanonicalizedResource
|
||
canoResource, err := buildCanonicalResource(uri)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 4. Build the signature string
|
||
signStr := buildSignString(method, contentMD5, contentType, date, canoHeaders, canoResource)
|
||
|
||
// 5. Calculate HMAC-SHA1 signature and encode with Base64
|
||
// Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString),AccessKeySecret))
|
||
return calculateHmacSha1(signStr, project.AccessKeySecret)
|
||
}
|
||
|
||
// getHeaderSafe safely retrieves a header value, returns empty string if not exists
|
||
func getHeaderSafe(headers map[string]string, key string) string {
|
||
if val, ok := headers[key]; ok {
|
||
return val
|
||
}
|
||
return ""
|
||
}
|
||
|
||
// buildCanonicalHeaders constructs normalized SLS headers
|
||
func buildCanonicalHeaders(headers map[string]string) string {
|
||
slsHeaders := make(map[string]string)
|
||
var slsHeaderKeys sort.StringSlice
|
||
|
||
// Extract headers prefixed with x-log- and x-acs-
|
||
for k, v := range headers {
|
||
l := strings.TrimSpace(strings.ToLower(k))
|
||
if strings.HasPrefix(l, "x-log-") || strings.HasPrefix(l, "x-acs-") {
|
||
slsHeaders[l] = strings.TrimSpace(v)
|
||
slsHeaderKeys = append(slsHeaderKeys, l)
|
||
}
|
||
}
|
||
|
||
// Sort headers alphabetically
|
||
sort.Sort(slsHeaderKeys)
|
||
|
||
// Build the canonical header string
|
||
var result strings.Builder
|
||
for i, k := range slsHeaderKeys {
|
||
result.WriteString(k)
|
||
result.WriteString(":")
|
||
result.WriteString(slsHeaders[k])
|
||
if i+1 < len(slsHeaderKeys) {
|
||
result.WriteString("\n")
|
||
}
|
||
}
|
||
|
||
return result.String()
|
||
}
|
||
|
||
// buildCanonicalResource constructs a normalized resource string
|
||
func buildCanonicalResource(uri string) (string, error) {
|
||
u, err := url.Parse(uri)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
result := u.Path
|
||
|
||
// Process query parameters
|
||
if u.RawQuery != "" {
|
||
var keys sort.StringSlice
|
||
vals := u.Query()
|
||
|
||
for k := range vals {
|
||
keys = append(keys, k)
|
||
}
|
||
|
||
sort.Sort(keys)
|
||
result += "?"
|
||
|
||
for i, k := range keys {
|
||
if i > 0 {
|
||
result += "&"
|
||
}
|
||
|
||
for _, v := range vals[k] {
|
||
result += k + "=" + v
|
||
}
|
||
}
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
// buildSignString constructs the complete signature string
|
||
func buildSignString(method, contentMD5, contentType, date, canoHeaders, canoResource string) string {
|
||
// SignString = VERB + "\n"
|
||
// + CONTENT-MD5 + "\n"
|
||
// + CONTENT-TYPE + "\n"
|
||
// + DATE + "\n"
|
||
// + CanonicalizedSLSHeaders + "\n"
|
||
// + CanonicalizedResource
|
||
return method + "\n" +
|
||
contentMD5 + "\n" +
|
||
contentType + "\n" +
|
||
date + "\n" +
|
||
canoHeaders + "\n" +
|
||
canoResource
|
||
}
|
||
|
||
// calculateHmacSha1 calculates the HMAC-SHA1 signature and encodes it with Base64
|
||
func calculateHmacSha1(signStr, secret string) (string, error) {
|
||
mac := hmac.New(sha1.New, []byte(secret))
|
||
_, err := mac.Write([]byte(signStr))
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return base64.StdEncoding.EncodeToString(mac.Sum(nil)), nil
|
||
}
|