すべてのプロダクト
Search
ドキュメントセンター

Function Compute:カスタムドメイン名の署名認証の設定

最終更新日:Sep 11, 2024

Function Computeでは、HTTPトリガーの署名認証を設定できます。 API GatewayがFunction Computeをバックエンドサービスとして使用するAPI操作からリクエストを受信すると、API Gatewayは署名認証機能が有効になっているカスタムドメイン名でリクエストを認証します。 これにより、関数はリクエスト署名を認証する必要がなく、ビジネスロジックのみに集中できます。 このトピックでは、Function Computeコンソールでカスタムドメイン名の署名認証を設定する方法について説明します。

あなたが始める前に

カスタムドメイン名の作成

手順

  1. Function Compute コンソールにログインします。 左側のナビゲーションウィンドウで、[詳細機能] > [カスタムドメイン] を選択します。

  2. 上部のナビゲーションバーで、管理するカスタムドメイン名が存在するリージョンを選択します。 [カスタムドメイン] ページで、管理するカスタムドメイン名をクリックします。

  3. 表示されるページの右上隅で、[変更] をクリックします。 [認証設定] セクションで、[認証方法][署名認証] に設定し、[保存] をクリックします。

    image.png

検証

オンプレミスマシンでコードを記述し、そのコードを実行します。 次の図は、コード構造を示しています。

image

サンプルコード:

  • signature.go

    package sign
    
    import (
    	"bytes"
    	"crypto/hmac"
    	"crypto/sha1"
    	"encoding/base64"
    	"hash"
    	"io"
    	"net/http"
    	"sort"
    	"strings"
    )
    
    // GetPOPAuthStr ... GetAuthStr get signature strings
    //
    //	@param accessKeyID
    //	@param accessKeySecret
    //	@param req
    //	@return string
    func GetPOPAuthStr(accessKeyID string, accessKeySecret string, req *http.Request) string {
    	return "acs " + accessKeyID + ":" + GetPOPSignature(accessKeySecret, req)
    }
    
    // GetPOPSignature ... ...
    //
    //	@param akSecret
    //	@param req
    //	@return string
    func GetPOPSignature(akSecret string, req *http.Request) string {
    	stringToSign := getStringToSign(req)
    	// fmt.Printf("stringToSign: %s\n", stringToSign)
    	return GetROASignature(stringToSign, akSecret)
    }
    
    // GetROASignature ... ...
    //
    //	@param stringToSign
    //	@param secret
    //	@return string
    func GetROASignature(stringToSign string, secret string) string {
    	h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(secret))
    	io.WriteString(h, stringToSign)
    	signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
    	return signedStr
    }
    
    // getStringToSign ...
    func getStringToSign(req *http.Request) string {
    	queryParams := make(map[string]string)
    	for k, v := range req.URL.Query() {
    		queryParams[k] = v[0]
    	}
    	// sort QueryParams by key
    	var queryKeys []string
    	for key := range queryParams {
    		queryKeys = append(queryKeys, key)
    	}
    	sort.Strings(queryKeys)
    	tmp := ""
    	for i := 0; i < len(queryKeys); i++ {
    		queryKey := queryKeys[i]
    		v := queryParams[queryKey]
    		if v != "" {
    			tmp = tmp + "&" + queryKey + "=" + v
    		} else {
    			tmp = tmp + "&" + queryKey
    		}
    	}
    	resource := req.URL.EscapedPath()
    	if tmp != "" {
    		tmp = strings.TrimLeft(tmp, "&")
    		resource = resource + "?" + tmp
    	}
    	return getSignedStr(req, resource)
    }
    
    func getSignedStr(req *http.Request, canonicalizedResource string) string {
    	temp := make(map[string]string)
    
    	for k, v := range req.Header {
    		if strings.HasPrefix(strings.ToLower(k), "x-acs-") {
    			temp[strings.ToLower(k)] = v[0]
    		}
    	}
    	hs := newSorter(temp)
    
    	// Sort the temp by the ascending order
    	hs.Sort()
    
    	// Get the canonicalizedOSSHeaders
    	canonicalizedOSSHeaders := ""
    	for i := range hs.Keys {
    		canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
    	}
    
    	// Give other parameters values
    	// when sign URL, date is expires
    	date := req.Header.Get("Date")
    	accept := req.Header.Get("Accept")
    	contentType := req.Header.Get("Content-Type")
    	contentMd5 := req.Header.Get("Content-MD5")
    
    	signStr := req.Method + "\n" + accept + "\n" + contentMd5 + "\n" + contentType + "\n" +
    		date + "\n" + canonicalizedOSSHeaders + canonicalizedResource
    	return signStr
    }
    
    // Sorter ...
    type Sorter struct {
    	Keys []string
    	Vals []string
    }
    
    func newSorter(m map[string]string) *Sorter {
    	hs := &Sorter{
    		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
    }
    
    // Sort is an additional function for function SignHeader.
    func (hs *Sorter) Sort() {
    	sort.Sort(hs)
    }
    
    // Len is an additional function for function SignHeader.
    func (hs *Sorter) Len() int {
    	return len(hs.Vals)
    }
    
    // Less is an additional function for function SignHeader.
    func (hs *Sorter) Less(i, j int) bool {
    	return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0
    }
    
    // Swap is an additional function for function SignHeader.
    func (hs *Sorter) 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]
    }
  • go.mo d

    module auth.fc.aliyun.com
    
    go 1.17
  • main.go

    次のサンプルコードでは、環境変数を使用して、呼び出し用のAccessKeyペアを取得します。 この方法は参考用です。 より安全なSecurity Token Service (STS) を使用することを推奨します。 詳細については、「AccessKeyの作成」および「アクセス資格情報の管理」をご参照ください。

    package main
    
    import (
    	"bytes"
    	"encoding/json"
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"os"
    	"time"
    
    	"auth.fc.aliyun.com/sign"
    )
    
    func main() {
    	// The request URL. Specify the URL based on your business requirements.
    	url := "A custom domain name or the endpoint of the HTTP trigger"
     
            // In this example, the AccessKey ID and AccessKey secret are stored in environment variables to implement identity authentication. 
      // Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured. 
    	// If the project code is leaked, the AccessKey pair may be disclosed, which may compromise the security of resources in your account. 
    	ak := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
    	sk := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
      
    	// Specify the data that you want to send.
    	data := map[string]interface{}{
    		"user": "FC 3.0",
    	}
    	jsonData, err := json.Marshal(data)
    	if err != nil {
    		fmt.Printf("Error encoding JSON: %s\n", err)
    		return
    	}
    
    	// Create a sample request.
    	request, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
    	if err != nil {
    		fmt.Printf("Error creating request: %s\n", err)
    		return
    	}
    
    	// Add a request header.
    	request.Header.Set("Content-Type", "application/json")
    
    	addAuthInfo(request, ak, sk)
    
    	// Create an HTTP client and send the request.
    	client := &http.Client{}
    	response, err := client.Do(request)
    	if err != nil {
    		fmt.Printf("Error sending request to server: %s\n", err)
    		return
    	}
    	defer response.Body.Close()
    
    	// Read the content of the response that is returned.
    	body, err := ioutil.ReadAll(response.Body)
    	if err != nil {
    		fmt.Printf("Error reading response body: %s\n", err)
    		return
    	}
    
    	// Print the content of the response.
    	fmt.Printf("Response Status: %s\n", response.Status)
    	fmt.Printf("Response Body: %s\n", string(body))
    }
    
    func addAuthInfo(req *http.Request, ak, sk string) {
    	if req.Header.Get("Date") == "" {
    		req.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
    	}
    	// copy from lambda-go-sdk sign-url-request
    	if req.URL.Path == "" {
    		req.URL.Path = "/"
    	}
    	authHeader := sign.GetPOPAuthStr(ak, sk, req)
    	req.Header.Set("Authorization", authHeader)
    }

次のコマンド出力は、関数の応答が取得されたことを示します。

Response Status: 200 OK
Response Body: Hello World!

よくある質問

ドメイン名の署名認証を有効にした後、カスタムドメイン名を使用して関数にアクセスすると、「必須HTTPヘッダー日付が指定されていません」が返されるのはなぜですか。

メッセージは、認証が失敗したことを示す。 考えられる原因は次のとおりです。

  1. リクエストには署名が含まれていません。

  2. リクエストには署名が含まれていますが、Dateヘッダーは含まれていません。

ドメイン名の署名認証を有効にした後、カスタムドメイン名を使用して機能にアクセスすると、「リクエスト時間「Thu, 04 Jan 2024 01:33:13 GMT」と現在の時間「Thu, 04 Jan 2024 08:34:58 GMT」の差が大きすぎる」と返されるのはなぜですか。

メッセージは、署名が期限切れになったことを示す。 現在の時点を使用して、リクエストに再署名します。

なぜ "私たちが計算したリクエスト署名があなたが提供した署名と一致しません。 ドメイン名の署名認証を有効にした後、カスタムドメイン名を使用して関数にアクセスするときに返されるアクセスキーと署名方法を確認しますか?

リクエストの署名がFunction Computeによって計算された署名と一致しないため、認証は失敗します。