Todos os produtos
Search
Central de documentação

Function Compute:Configure signature authentication for custom domain names

Última atualização: Jun 29, 2026

O Function Compute permite configurar a autenticação por assinatura para gatilhos HTTP. Ao ativar esse recurso em um nome de domínio personalizado, o API Gateway valida a assinatura de cada solicitação recebida antes de encaminhá-la à sua função. Isso descarrega a autenticação da função, permitindo que você se concentre na lógica de negócios.

Ative a autenticação por assinatura quando desejar que apenas chamadores com uma AccessKey válida invoquem sua função por meio de um nome de domínio personalizado. Mantenha-a desativada para endpoints públicos sem autenticação.

Pré-requisitos

Antes de começar, verifique se você já:

Ativar autenticação por assinatura

  1. Acesse o console do Function Compute. No painel de navegação à esquerda, escolha Function Management > Custom Domain Name.

  2. Na barra de navegação superior, selecione a região onde reside o nome de domínio personalizado. Na página Custom Domains, clique em no nome de domínio personalizado.

  3. No canto superior direito, clique em Modify. Na seção Authentication Settings, defina Authentication Method como Signature Authentication e clique em Save.

Authentication Settings section with Signature Authentication selected

Verificar a configuração

Após ativar a autenticação por assinatura, escreva o código na máquina local e execute-o para enviar uma solicitação assinada e confirmar se o API Gateway a aceita. O exemplo abaixo usa Go para criar e enviar essa solicitação.

O projeto de exemplo contém dois arquivos:

Arquivo

Finalidade

signature.go

Implementa o algoritmo de assinatura

main.go

Envia uma solicitação POST assinada para o endpoint da função

Sample project file structure

Funcionamento do algoritmo de assinatura

O algoritmo de assinatura constrói o cabeçalho Authorization em quatro etapas:

  1. Construir a string canônica: concatene o método HTTP e os cabeçalhos Accept, Content-MD5, Content-Type e Date. Em seguida, adicione os cabeçalhos x-acs-* ordenados (CanonicalizedOSSHeaders) e o recurso canônico (caminho da URL + parâmetros de consulta ordenados).

  2. Calcular a assinatura HMAC-SHA1: aplique HMAC-SHA1 usando o segredo da AccessKey como chave e codifique o resultado em Base64.

  3. Formatar a string de autorização: combine o resultado no formato acs <AccessKey ID>:<signature>.

  4. Anexar o cabeçalho: defina Authorization: acs <AccessKey ID>:<signature> na solicitação.

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

O exemplo a seguir lê credenciais de variáveis de ambiente e envia uma solicitação POST assinada para o endpoint da função.

Este exemplo usa credenciais de AccessKey de longo prazo para simplificar. Para cargas de trabalho em produção, use o Security Token Service (STS). Consulte Criar uma AccessKey e Gerenciar credenciais de acesso para obter detalhes.
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)
}

Uma resposta bem-sucedida apresenta o seguinte formato:

Response Status: 200 OK
Response Body: Hello World!

Solucionar erros comuns

"Required HTTP header Date was not specified"

Causa: A solicitação não possui o cabeçalho Date, o cabeçalho Authorization ou ambos.

Solução:

  1. Se a solicitação não tiver o cabeçalho Authorization, adicione a lógica de assinatura usando o código de exemplo acima.

  2. Caso o cabeçalho Authorization esteja presente, mas o Date esteja ausente, adicione um cabeçalho Date com a hora UTC atual no formato RFC 7231. Exemplo: Thu, 04 Jan 2024 01:33:13 GMT. A função addAuthInfo em main.go define isso automaticamente.

"The difference between the request time and the current time is too large"

Causa: A assinatura expirou. Assine novamente a solicitação usando a hora atual do sistema.

"The request signature we calculated does not match the signature you provided"

Causa: A assinatura na solicitação não corresponde àquela calculada pelo Function Compute com as mesmas entradas.

Solução: Verifique os itens abaixo nesta ordem:

  1. Credenciais: confirme se o AccessKey ID e o segredo da AccessKey nas variáveis de ambiente estão corretos.

  2. Ordem da string canônica: compare sua implementação passo a passo com a função getStringToSign. A ordem obrigatória é: método HTTP → AcceptContent-MD5Content-TypeDate → cabeçalhos x-acs-* ordenados → recurso canônico.

  3. Formato de autorização: certifique-se de que o valor do cabeçalho Authorization segue o formato acs <AccessKey ID>:<signature>. Verifique a saída de GetPOPAuthStr.