全部文档
当前文档

暂无内容

如果没有找到您期望的内容,请尝试其他搜索词

文档中心

Go SDK签名相关Demo

最近更新时间:2024-03-19 19:20:44

使用KS3时,所有发送的非匿名请求都需要携带签名,KS3服务器端收到消息后,进行身份验证,验证成功则接受并执行请求,否则将会返回403错误信息并丢弃此请求。本文主要介绍V2签名算法在Go语言中的实现方式,并且通过Authorization Header来携带签名信息。

计算签名

本部分代码介绍如何在Go语言中计算请求签名,KS3主要提供了两个函数。

1. SignV2() 函数用于构造V2请求签名

如下所示为该函数对应的参数信息:

参数名称

描述

ak

您的AccessKeyID,必填。

sk

您的SecretAccessKey,必填。

bucket

访问的存储空间名称,如:test-bucket。若您想访问全部存储空间,则此处传空字符。

objectKey

访问的对象,如:demo.txt。若您只想针对存储空间进行操作,则此处传空字符。

subResource

子资源,如:acl,policy等。若没有则此处传空字符。

req

请求对象,必填。

如下所示为该函数对应的返回值信息:

返回值名称

描述

Authorization

构造出的Authorization请求头的值。

2. GetUrl() 函数用于构造请求的URL

如下所示为该函数对应的参数信息:

参数名称

描述

endpoint

访问域名,如:ks3-cn-beijing.ksyuncs.com,必填。

bucket

访问的存储空间名称,如:test-bucket。若您想访问全部存储空间,则此处传空字符。

objectKey

访问的对象,如:demo.txt。若若您只想针对存储空间进行操作,则此处传空字符。

subResource

子资源,如:acl,policy等。若没有则此处传空字符。

query

查询参数,如:prefix=test&max-keys=100。若没有则此处传空字符。

如下所示为该函数对应的返回值信息:

返回值名称

描述

URL

构造出的请求URL值。

如下所示为详细代码示例:

package main

import (
    "bytes"
    "crypto/hmac"
    "crypto/sha1"
    "encoding/base64"
    "fmt"
    "hash"
    "io"
    "net/http"
    "net/url"
    "sort"
    "strings"
)

// SignV2 构造V2请求签名
// ak 您的AccessKeyID,必填
// sk 您的SecretAccessKey,必填
// bucket 访问的存储空间名称,如:test-bucket。若您想ListBuckets,则此处传空字符
// objectKey 访问的对象,如:demo.txt。若您是针对bucket进行的操作,则此处传空字符
// subResource 子资源,如:acl,policy等。若没有则此处传空字符
// req 请求对象,必填
func SignV2(ak string, sk string, bucket string, objectKey string, subResource string, req *http.Request) string {
    if ak == "" || sk == "" {
        return ""
    }
    // 获取规范化请求资源
    canonicalizedResource := getCanonicalizedResource(bucket, objectKey, subResource)
    // 获取规范化x-kss-请求头
    canonicalizedKssHeaders := getCanonicalizedKssHeaders(req)
    // 获取待签名字符串
    stringToSign := getStringToSign(req, canonicalizedResource, canonicalizedKssHeaders)
    // 获取签名
    signature := getSignature(stringToSign, sk)
    // 构造Authorization请求头
    Authorization := "KSS " + ak + ":" + signature

    return Authorization
}

func getCanonicalizedResource(bucketName, objectName, subResource string) string {
    if subResource != "" {
        subResource = "?" + subResource
    }
    if bucketName == "" {
        return fmt.Sprintf("/%s%s", bucketName, subResource)
    }
    objectName = encodeKS3Str(objectName)
    resource := "/" + bucketName + "/" + objectName + subResource
    return resource
}

func getCanonicalizedKssHeaders(req *http.Request) string {
    ks3HeadersMap := make(map[string]string)
    for k, v := range req.Header {
        if strings.HasPrefix(strings.ToLower(k), "x-kss-") {
            ks3HeadersMap[strings.ToLower(k)] = v[0]
        }
    }
    hs := newHeaderSorter(ks3HeadersMap)

    hs.Sort()

    canonicalizedKssHeaders := ""

    for i := range hs.Keys {
        canonicalizedKssHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
    }

    return canonicalizedKssHeaders
}

func getStringToSign(req *http.Request, canonicalizedResource string, canonicalizedKssHeaders string) string {
    date := req.Header.Get("Date")
    contentType := req.Header.Get("Content-Type")
    contentMd5 := req.Header.Get("Content-MD5")

    stringToSign := req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedKssHeaders + canonicalizedResource

    return stringToSign
}

func getSignature(stringToSign string, sk string) string {
    h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(sk))
    io.WriteString(h, stringToSign)

    signature := base64.StdEncoding.EncodeToString(h.Sum(nil))

    return signature
}

func encodeKS3Str(str string) string {
    objectName := url.QueryEscape(str)
    objectName = strings.ReplaceAll(objectName, "+", "%20")
    objectName = strings.ReplaceAll(objectName, "*", "%2A")
    objectName = strings.ReplaceAll(objectName, "%7E", "~")
    objectName = strings.ReplaceAll(objectName, "%2F", "/")
    if strings.HasPrefix(objectName, "/") {
        objectName = strings.Replace(objectName, "/", "%2F", 1)
    }
    objectName = strings.ReplaceAll(objectName, "//", "/%2F")
    return objectName
}

func newHeaderSorter(m map[string]string) *HeaderSorter {
    hs := &HeaderSorter{
        Keys: make([]string, 0, len(m)),
        Vals: make([]string, 0, len(m)),
    }

    for k, v := range m {
        hs.Keys = append(hs.Keys, k)
        hs.Vals = append(hs.Vals, v)
    }
    return hs
}

type HeaderSorter struct {
    Keys []string
    Vals []string
}

func (hs *HeaderSorter) Sort() {
    sort.Sort(hs)
}

func (hs *HeaderSorter) Len() int {
    return len(hs.Vals)
}

func (hs *HeaderSorter) Less(i, j int) bool {
    return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0
}

func (hs *HeaderSorter) Swap(i, j int) {
    hs.Vals[i], hs.Vals[j] = hs.Vals[j], hs.Vals[i]
    hs.Keys[i], hs.Keys[j] = hs.Keys[j], hs.Keys[i]
}

// GetUrl 构造请求的URL
// endpoint 访问域名,如:ks3-cn-beijing.ksyuncs.com,必填
// bucket 访问的存储空间名称,如:test-bucket。若您想ListBuckets,则此处传空字符
// objectKey 访问的对象,如:demo.txt。若您是针对bucket进行的操作,则此处传空字符
// subResource 子资源,如:acl,policy等。若没有则此处传空字符
// query 查询参数,如:prefix=test&max-keys=100。若没有则此处传空字符
func GetUrl(endpoint string, bucket string, objectKey string, subResource string, query string) string {
    queryString := ""
    if subResource != "" {
        queryString = "?" + subResource
    }
    if query != "" {
        queryString = "?" + query
    }
    if subResource != "" && query != "" {
        queryString = "?" + subResource + "&" + query
    }
    resource := encodeKS3Str(objectKey) + queryString
    if bucket == "" {
        return fmt.Sprintf("https://%s/%s", endpoint, resource)
    }
    return fmt.Sprintf("https://%s.%s/%s", bucket, endpoint, resource)
}

使用签名

使用签名的主要步骤如下:

  • 填写AK,SK等信息

  • 构造请求URL

  • 构造HTTP请求

  • 计算签名

  • 添加Authorization请求头

  • 发送请求

以下为使用签名的详细代码示例:

package main

import (
    "fmt"
    "net/http"
    "strings"
    "time"
)

func main() {
    // 填写AK
    ak := "AccessKeyID"
    // 填写SK
    sk := "SecretAccessKey"
    // 填写访问域名
    endpoint := "ks3-cn-beijing.ksyuncs.com"
    // 填写bucket的名称
    bucket := "bucketName"

    fmt.Println("---------------- 上传对象 ----------------")
    putObject(ak, sk, endpoint, bucket)

    fmt.Println("---------------- 获取对象 ----------------")
    getObject(ak, sk, endpoint, bucket)

    fmt.Println("---------------- 获取对象的ACL ----------------")
    getObjectAcl(ak, sk, endpoint, bucket)

    fmt.Println("---------------- 删除对象 ----------------")
    deleteObject(ak, sk, endpoint, bucket)

    fmt.Println("---------------- 列举bucket ----------------")
    listBucket(ak, sk, endpoint)

    fmt.Println("---------------- 列举object ----------------")
    listObject(ak, sk, endpoint, bucket)
}

func putObject(ak string, sk string, endpoint string, bucket string) {
    objectKey := "demo.txt"
    // 构造请求URL
    url := GetUrl(endpoint, bucket, objectKey, "", "")
    fmt.Println("requestURL: ", url)
    // 构造HTTP请求
    req, err := http.NewRequest("PUT", url, strings.NewReader("test content"))
    if err != nil {
        panic(err)
    }
    // Date 表示此次请求操作的时间,必须为 HTTP1.1 中支持的 GMT 格式,例如:Tue, 30 Nov 2021 06:29:38 GMT。
    req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
    req.Header.Add("Content-Type", "text/plain")
    req.Header.Add("x-kss-acl", "public-read")
    // 计算签名
    Authorization := SignV2(ak, sk, bucket, objectKey, "", req)
    fmt.Println("Authorization: ", Authorization)
    // 添加Authorization请求头
    req.Header.Set("Authorization", Authorization)
    // 发送请求
    client := http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    fmt.Println("StatusCode: ", resp.StatusCode)
    fmt.Println("Status: ", resp.Status)
}

func getObject(ak string, sk string, endpoint string, bucket string) {
    objectKey := "demo.txt"
    // 构造请求URL
    url := GetUrl(endpoint, bucket, objectKey, "", "")
    fmt.Println("requestURL: ", url)
    // 构造HTTP请求
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        panic(err)
    }
    // Date 表示此次请求操作的时间,必须为 HTTP1.1 中支持的 GMT 格式,例如:Tue, 30 Nov 2021 06:29:38 GMT。
    req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
    // 计算签名
    Authorization := SignV2(ak, sk, bucket, objectKey, "", req)
    fmt.Println("Authorization: ", Authorization)
    // 添加Authorization请求头
    req.Header.Set("Authorization", Authorization)
    // 发送请求
    client := http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    fmt.Println("StatusCode: ", resp.StatusCode)
    fmt.Println("Status: ", resp.Status)
}

func getObjectAcl(ak string, sk string, endpoint string, bucket string) {
    objectKey := "demo.txt"
    subResource := "acl"
    // 构造请求URL
    url := GetUrl(endpoint, bucket, objectKey, subResource, "")
    fmt.Println("requestURL: ", url)
    // 构造HTTP请求
    req, err := http.NewRequest("GET", url, strings.NewReader("test content"))
    if err != nil {
        panic(err)
    }
    // Date 表示此次请求操作的时间,必须为 HTTP1.1 中支持的 GMT 格式,例如:Tue, 30 Nov 2021 06:29:38 GMT。
    req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
    // 计算签名
    Authorization := SignV2(ak, sk, bucket, objectKey, subResource, req)
    fmt.Println("Authorization: ", Authorization)
    // 添加Authorization请求头
    req.Header.Set("Authorization", Authorization)
    // 发送请求
    client := http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    fmt.Println("StatusCode: ", resp.StatusCode)
    fmt.Println("Status: ", resp.Status)
}

func deleteObject(ak string, sk string, endpoint string, bucket string) {
    objectKey := "demo.txt"
    // 构造请求URL
    url := GetUrl(endpoint, bucket, objectKey, "", "")
    fmt.Println("requestURL: ", url)
    // 构造HTTP请求
    req, err := http.NewRequest("DELETE", url, nil)
    if err != nil {
        panic(err)
    }
    // Date 表示此次请求操作的时间,必须为 HTTP1.1 中支持的 GMT 格式,例如:Tue, 30 Nov 2021 06:29:38 GMT。
    req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
    // 计算签名
    Authorization := SignV2(ak, sk, bucket, objectKey, "", req)
    fmt.Println("Authorization: ", Authorization)
    // 添加Authorization请求头
    req.Header.Set("Authorization", Authorization)
    // 发送请求
    client := http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    fmt.Println("StatusCode: ", resp.StatusCode)
    fmt.Println("Status: ", resp.Status)
}

func listBucket(ak string, sk string, endpoint string) {
    // 构造请求URL
    url := GetUrl(endpoint, "", "", "", "")
    fmt.Println("requestURL: ", url)
    // 构造HTTP请求
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        panic(err)
    }
    // Date 表示此次请求操作的时间,必须为 HTTP1.1 中支持的 GMT 格式,例如:Tue, 30 Nov 2021 06:29:38 GMT。
    req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
    // 计算签名
    Authorization := SignV2(ak, sk, "", "", "", req)
    fmt.Println("Authorization: ", Authorization)
    // 添加Authorization请求头
    req.Header.Set("Authorization", Authorization)
    // 发送请求
    client := http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    fmt.Println("StatusCode: ", resp.StatusCode)
    fmt.Println("Status: ", resp.Status)
}

func listObject(ak string, sk string, endpoint string, bucket string) {
    query := "prefix=test&max-keys=100"
    // 构造请求URL
    url := GetUrl(endpoint, bucket, "", "", query)
    fmt.Println("requestURL: ", url)
    // 构造HTTP请求
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        panic(err)
    }
    // Date 表示此次请求操作的时间,必须为 HTTP1.1 中支持的 GMT 格式,例如:Tue, 30 Nov 2021 06:29:38 GMT。
    req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
    // 计算签名
    Authorization := SignV2(ak, sk, bucket, "", "", req)
    fmt.Println("Authorization: ", Authorization)
    // 添加Authorization请求头
    req.Header.Set("Authorization", Authorization)
    // 发送请求
    client := http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    fmt.Println("StatusCode: ", resp.StatusCode)
    fmt.Println("Status: ", resp.Status)
}

文档导读
纯净模式常规模式

纯净模式

点击可全屏预览文档内容
文档反馈