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 は署名文字列を取得します // // @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] } // キーで QueryParams をソートします 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) // temp を昇順でソートします hs.Sort() // canonicalizedOSSHeaders を取得します canonicalizedOSSHeaders := "" for i := range hs.Keys { canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n" } // 他のパラメーター値を与えます // URL に署名する場合、日付は有効期限です 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 は SignHeader 関数の追加機能です。 func (hs *Sorter) Sort() { sort.Sort(hs) } // Len は SignHeader 関数の追加機能です。 func (hs *Sorter) Len() int { return len(hs.Vals) } // Less は SignHeader 関数の追加機能です。 func (hs *Sorter) Less(i, j int) bool { return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0 } // Swap は 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.mod
module auth.fc.aliyun.com go 1.17main.go
次のサンプルコードでは、環境変数を使用して呼び出し用の AccessKey ペアを取得します。このメソッドは参考用です。より安全なセキュリティトークンサービス (STS) を使用することをお勧めします。詳細については、「AccessKey の作成」および「アクセス資格情報の管理」をご参照ください。
package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "os" "time" "auth.fc.aliyun.com/sign" ) func main() { // リクエスト URL。必要に応じて URL を指定します。 url := "A custom domain name or the endpoint of the HTTP trigger" // この例では、AccessKey ID と AccessKey Secret は、身分認証を実装するために環境変数に格納されます。 // ALIBABA_CLOUD_ACCESS_KEY_ID および ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。 // プロジェクトコードが漏洩すると、AccessKey ペアが漏洩し、アカウント内のリソースのセキュリティが損なわれる可能性があります。 ak := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID") sk := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET") // 送信するデータを指定します。 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 } // サンプルリクエストを作成します。 request, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { fmt.Printf("Error creating request: %s\n", err) return } // リクエストヘッダーを追加します。 request.Header.Set("Content-Type", "application/json") addAuthInfo(request, ak, sk) // HTTP クライアントを作成してリクエストを送信します。 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() // 返された応答のコンテンツを読み取ります。 body, err := ioutil.ReadAll(response.Body) if err != nil { fmt.Printf("Error reading response body: %s\n", err) return } // 応答のコンテンツを出力します。 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)) } // 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!よくある質問
ドメイン名の署名認証を有効にした後、カスタムドメイン名を使用して関数にアクセスすると、「required HTTP header Date was not specified」が返されるのはなぜですか。
このメッセージは、認証が失敗したことを示します。考えられる原因は次のとおりです。
リクエストに署名が含まれていません。
リクエストには署名が含まれていますが、Date ヘッダーが含まれていません。
ドメイン名の署名認証を有効にした後、カスタムドメイン名を使用して関数にアクセスすると、「the difference between the request time 'Thu, 04 Jan 2024 01:33:13 GMT' and the current time 'Thu, 04 Jan 2024 08:34:58 GMT' is too large」が返されるのはなぜですか。
このメッセージは、署名の有効期限が切れていることを示します。現在の時刻を使用してリクエストに再署名してください。
ドメイン名の署名認証を有効にした後、カスタムドメイン名を使用して関数にアクセスすると、「The request signature we calculated does not match the signature you provided. Check your access key and signing method」が返されるのはなぜですか。
リクエスト内の署名が Function Compute によって計算された署名と一致しないため、認証は失敗します。