Tous les produits
Search
Centre de documentation

Function Compute:Configurer l'authentification par signature pour les noms de domaine personnalisés

Dernière mise à jour :Aug 12, 2026

Function Compute permet de configurer l'authentification par signature pour les déclencheurs HTTP. Lorsque vous activez cette authentification sur un nom de domaine personnalisé, API Gateway valide la signature de chaque requête entrante avant de la transmettre à votre fonction. Cette délégation de l'authentification vous permet de vous concentrer sur la logique métier.

Activez l'authentification par signature si vous souhaitez que seuls les appelants disposant d'une AccessKey valide puissent invoquer votre fonction via un nom de domaine personnalisé. Laissez cette option désactivée pour les endpoints publics ne nécessitant pas d'authentification.

Prérequis

Avant de commencer, assurez-vous d'avoir :

Activer l'authentification par signature

  1. Connectez-vous à la console Function Compute. Dans le volet de navigation de gauche, choisissez Function Management > Custom Domain Name.

  2. Dans la barre de navigation supérieure, sélectionnez la région où se trouve le nom de domaine personnalisé. Sur la page Custom Domains, cliquez sur le nom de domaine personnalisé concerné.

  3. Dans le coin supérieur droit, cliquez sur Modify. Dans la section Authentication Settings, définissez Authentication Method sur Signature Authentication, puis cliquez sur Save.

Authentication Settings section with Signature Authentication selected

Vérifier la configuration

Une fois l'authentification par signature activée, écrivez du code sur votre machine locale et exécutez-le afin d'envoyer une requête signée et de confirmer qu'API Gateway l'accepte. L'exemple suivant utilise Go pour construire et envoyer une requête signée.

Le projet d'exemple contient deux fichiers :

Fichier Objectif
signature.go Implémente l'algorithme de signature
main.go Envoie une requête POST signée à l'endpoint de votre fonction

Sample project file structure

Fonctionnement de l'algorithme de signature

L'algorithme de signature construit l'en-tête Authorization en quatre étapes :

  1. Construction de la chaîne canonique — Concaténez la méthode HTTP ainsi que les en-têtes Accept, Content-MD5, Content-Type et Date. Ajoutez ensuite les en-têtes x-acs-* triés (CanonicalizedOSSHeaders), puis la ressource canonisée (chemin URL et paramètres de requête triés).

  2. Calcul de la signature HMAC-SHA1 — Appliquez HMAC-SHA1 en utilisant votre AccessKey secret comme clé, puis encodez le résultat en Base64.

  3. Formatage de la chaîne d'autorisation — Combinez le résultat sous la forme acs <AccessKey ID>:<signature>.

  4. Ajout de l'en-tête — Définissez Authorization: acs <AccessKey ID>:<signature> sur la requête.

signature.go

package sign

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

// GetPOPAuthStr returns the Authorization header value for a request.
func GetPOPAuthStr(accessKeyID string, accessKeySecret string, req *http.Request) string {
	return "acs " + accessKeyID + ":" + GetPOPSignature(accessKeySecret, req)
}

// GetPOPSignature computes the signature for a request.
func GetPOPSignature(akSecret string, req *http.Request) string {
	stringToSign := getStringToSign(req)
	return GetROASignature(stringToSign, akSecret)
}

// GetROASignature applies HMAC-SHA1 and Base64-encodes the result.
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 builds the canonical string from the request.
func getStringToSign(req *http.Request) string {
	queryParams := make(map[string]string)
	for k, v := range req.URL.Query() {
		queryParams[k] = v[0]
	}
	// Sort query parameters 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 x-acs-* headers alphabetically
	hs.Sort()

	// Build the canonicalized x-acs-* headers string
	canonicalizedOSSHeaders := ""
	for i := range hs.Keys {
		canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
	}

	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 sorts header keys and values together.
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
}

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

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

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

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

L'exemple suivant lit les identifiants depuis les variables d'environnement et envoie une requête POST signée à l'endpoint de votre fonction.

Cet exemple utilise des identifiants AccessKey permanents pour plus de simplicité. Pour les charges de travail en production, privilégiez Security Token Service (STS). Consultez Créer une AccessKey et Gérer les identifiants d'accès pour plus de détails.
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"auth.fc.aliyun.com/sign"
)

func main() {
	// Replace with your custom domain name or HTTP trigger endpoint.
	url := "A custom domain name or the endpoint of the HTTP trigger"

	// Read credentials from environment variables.
	// Make sure ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET are set.
	ak := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
	sk := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")

	// Build the request body.
	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 the request.
	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")

	// Sign the request and attach the Authorization header.
	addAuthInfo(request, ak, sk)

	// 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()

	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))
	}
	if req.URL.Path == "" {
		req.URL.Path = "/"
	}
	authHeader := sign.GetPOPAuthStr(ak, sk, req)
	req.Header.Set("Authorization", authHeader)
}

Une réponse réussie ressemble à ceci :

Response Status: 200 OK
Response Body: Hello World!

Résoudre les erreurs courantes

« Required HTTP header Date was not specified »

Cause : La requête ne contient pas l'en-tête Date, l'en-tête Authorization, ou aucun des deux.

Solution :

  1. Si la requête ne possède pas d'en-tête Authorization, ajoutez la logique de signature en utilisant le code d'exemple ci-dessus.

  2. Si l'en-tête Authorization est présent mais que Date est absent, ajoutez un en-tête Date correspondant à l'heure UTC actuelle au format RFC 7231, par exemple Thu, 04 Jan 2024 01:33:13 GMT. La fonction addAuthInfo dans main.go configure cela automatiquement.

« The difference between the request time and the current time is too large »

Cause : La signature a expiré. Signez à nouveau la requête en utilisant l'heure système actuelle.

« The request signature we calculated does not match the signature you provided »

Cause : La signature contenue dans la requête ne correspond pas à celle calculée par Function Compute à partir des mêmes entrées.

Solution : Vérifiez les points suivants dans l'ordre :

  1. Identifiants — Assurez-vous que l'AccessKey ID et l'AccessKey secret présents dans vos variables d'environnement sont corrects.

  2. Ordre de la chaîne canonique — Comparez votre implémentation étape par étape avec la fonction getStringToSign. L'ordre requis est : méthode HTTP → Accept → Content-MD5 → Content-Type → Date → en-têtes x-acs-* triés → ressource canonisée.

  3. Format d'autorisation — Confirmez que la valeur de l'en-tête Authorization respecte le format acs <AccessKey ID>:<signature>. Vérifiez la sortie de GetPOPAuthStr.