全部产品
Search
文档中心

Function Compute:Konfigurasikan Otentikasi Tanda Tangan untuk Nama Domain Kustom

更新时间:Nov 11, 2025

Function Compute memungkinkan Anda mengonfigurasi otentikasi tanda tangan untuk pemicu HTTP. Ketika API Gateway menerima permintaan dari operasi API yang menggunakan Function Compute sebagai layanan backend, API Gateway mengotentikasi permintaan tersebut pada nama domain kustom yang telah mengaktifkan fitur otentikasi tanda tangan. Dengan demikian, fungsi Anda tidak perlu lagi mengotentikasi tanda tangan permintaan, sehingga Anda dapat fokus sepenuhnya pada logika bisnis. Topik ini menjelaskan cara mengonfigurasi otentikasi tanda tangan untuk nama domain kustom di Konsol Function Compute.

Sebelum Anda mulai

Buat nama domain kustom.

Prosedur

  1. Masuk ke Konsol Function Compute. Di panel navigasi kiri, pilih Function Management > Custom Domains.

  2. Di bilah navigasi atas, pilih wilayah tempat nama domain kustom yang ingin Anda kelola berada. Pada halaman Domain Kustom, klik nama domain kustom yang ingin Anda kelola.

  3. Di pojok kanan atas halaman yang muncul, klik Modify. Di bagian Authentication Settings, atur Authentication Method menjadi Signature Authentication, lalu klik Save.

    image.png

Verifikasi

Tulis dan jalankan kode secara lokal. Gambar berikut menunjukkan struktur direktori kode.

image

Contoh kode:

  • signature.go

    package sign
    
    import (
    	"bytes"
    	"crypto/hmac"
    	"crypto/sha1"
    	"encoding/base64"
    	"hash"
    	"io"
    	"net/http"
    	"sort"
    	"strings"
    )
    
    // GetPOPAuthStr ... GetAuthStr mendapatkan string tanda tangan
    //
    //	@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]
    	}
    	// urutkan QueryParams berdasarkan kunci
    	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)
    
    	// Urutkan temp berdasarkan urutan naik
    	hs.Sort()
    
    	// Dapatkan canonicalizedOSSHeaders
    	canonicalizedOSSHeaders := ""
    	for i := range hs.Keys {
    		canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
    	}
    
    	// Berikan nilai parameter lainnya
    	// saat menandatangani URL, tanggal adalah masa berlaku
    	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 adalah fungsi tambahan untuk fungsi SignHeader.
    func (hs *Sorter) Sort() {
    	sort.Sort(hs)
    }
    
    // Len adalah fungsi tambahan untuk fungsi SignHeader.
    func (hs *Sorter) Len() int {
    	return len(hs.Vals)
    }
    
    // Less adalah fungsi tambahan untuk fungsi SignHeader.
    func (hs *Sorter) Less(i, j int) bool {
    	return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0
    }
    
    // Swap adalah fungsi tambahan untuk fungsi 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.17
  • main.go

    Contoh kode berikut menggunakan variabel lingkungan untuk mendapatkan pasangan AccessKey untuk pemanggilan. Metode ini hanya untuk referensi. Kami menyarankan Anda menggunakan Security Token Service (STS), yang lebih aman. Untuk informasi lebih lanjut, lihat Buat AccessKey dan Kelola kredensial akses.

    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 as needed.
    	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)
    }

Keluaran perintah berikut menunjukkan bahwa respons dari fungsi diperoleh.

Response Status: 200 OK
Response Body: Hello World!

FAQ

Mengapa "header HTTP yang diperlukan Date tidak ditentukan" dikembalikan ketika saya menggunakan nama domain kustom untuk mengakses fungsi setelah saya mengaktifkan otentikasi tanda tangan untuk nama domain tersebut?

Pesan tersebut menunjukkan bahwa otentikasi gagal. Berikut adalah kemungkinan penyebabnya:

  1. Permintaan tidak mengandung tanda tangan.

  2. Permintaan mengandung tanda tangan tetapi tidak mengandung header Date.

Mengapa "perbedaan antara waktu permintaan 'Thu, 04 Jan 2024 01:33:13 GMT' dan waktu saat ini 'Thu, 04 Jan 2024 08:34:58 GMT' terlalu besar" dikembalikan ketika saya menggunakan nama domain kustom untuk mengakses fungsi setelah saya mengaktifkan otentikasi tanda tangan untuk nama domain tersebut?

Pesan tersebut menunjukkan bahwa tanda tangan telah kedaluwarsa. Gunakan titik waktu saat ini untuk menandatangani ulang permintaan.

Mengapa "tanda tangan permintaan yang kami hitung tidak cocok dengan tanda tangan yang Anda berikan. Periksa kunci akses dan metode penandatanganan Anda" dikembalikan ketika saya menggunakan nama domain kustom untuk mengakses fungsi setelah saya mengaktifkan otentikasi tanda tangan untuk nama domain tersebut?

Otentikasi gagal karena tanda tangan dalam permintaan tidak sesuai dengan tanda tangan yang dihitung oleh Function Compute.