Function Computeでは、HTTPトリガーの署名認証を設定できます。 API GatewayがFunction Computeをバックエンドサービスとして使用するAPI操作からリクエストを受信すると、API Gatewayは署名認証機能が有効になっているカスタムドメイン名でリクエストを認証します。 これにより、関数はリクエスト署名を認証する必要がなく、ビジネスロジックのみに集中できます。 このトピックでは、Function Computeコンソールでカスタムドメイン名の署名認証を設定する方法について説明します。
あなたが始める前に
手順
Function Compute コンソールにログインします。 左側のナビゲーションウィンドウで、 を選択します。
上部のナビゲーションバーで、管理するカスタムドメイン名が存在するリージョンを選択します。 [カスタムドメイン] ページで、管理するカスタムドメイン名をクリックします。
表示されるページの右上隅で、[変更] をクリックします。 [認証設定] セクションで、[認証方法] を [署名認証] に設定し、[保存] をクリックします。
検証
オンプレミスマシンでコードを記述し、そのコードを実行します。 次の図は、コード構造を示しています。
サンプルコード:
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ヘッダー日付が指定されていません」が返されるのはなぜですか。
メッセージは、認証が失敗したことを示す。 考えられる原因は次のとおりです。
リクエストには署名が含まれていません。
リクエストには署名が含まれていますが、Dateヘッダーは含まれていません。
ドメイン名の署名認証を有効にした後、カスタムドメイン名を使用して機能にアクセスすると、「リクエスト時間「Thu, 04 Jan 2024 01:33:13 GMT」と現在の時間「Thu, 04 Jan 2024 08:34:58 GMT」の差が大きすぎる」と返されるのはなぜですか。
メッセージは、署名が期限切れになったことを示す。 現在の時点を使用して、リクエストに再署名します。
なぜ "私たちが計算したリクエスト署名があなたが提供した署名と一致しません。 ドメイン名の署名認証を有効にした後、カスタムドメイン名を使用して関数にアクセスするときに返されるアクセスキーと署名方法を確認しますか?
リクエストの署名がFunction Computeによって計算された署名と一致しないため、認証は失敗します。