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
Acesse o console do Function Compute. No painel de navegação à esquerda, escolha Function Management > Custom Domain Name.
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.
No canto superior direito, clique em Modify. Na seção Authentication Settings, defina Authentication Method como Signature Authentication e clique em Save.

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 |
|
|
Implementa o algoritmo de assinatura |
|
|
Envia uma solicitação POST assinada para o endpoint da função |

Funcionamento do algoritmo de assinatura
O algoritmo de assinatura constrói o cabeçalho Authorization em quatro etapas:
Construir a string canônica: concatene o método HTTP e os cabeçalhos
Accept,Content-MD5,Content-TypeeDate. Em seguida, adicione os cabeçalhosx-acs-*ordenados (CanonicalizedOSSHeaders) e o recurso canônico (caminho da URL + parâmetros de consulta ordenados).Calcular a assinatura HMAC-SHA1: aplique HMAC-SHA1 usando o segredo da AccessKey como chave e codifique o resultado em Base64.
Formatar a string de autorização: combine o resultado no formato
acs <AccessKey ID>:<signature>.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:
Se a solicitação não tiver o cabeçalho
Authorization, adicione a lógica de assinatura usando o código de exemplo acima.Caso o cabeçalho
Authorizationesteja presente, mas oDateesteja ausente, adicione um cabeçalhoDatecom a hora UTC atual no formato RFC 7231. Exemplo:Thu, 04 Jan 2024 01:33:13 GMT. A funçãoaddAuthInfoemmain.godefine 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:
Credenciais: confirme se o AccessKey ID e o segredo da AccessKey nas variáveis de ambiente estão corretos.
Ordem da string canônica: compare sua implementação passo a passo com a função
getStringToSign. A ordem obrigatória é: método HTTP →Accept→Content-MD5→Content-Type→Date→ cabeçalhosx-acs-*ordenados → recurso canônico.Formato de autorização: certifique-se de que o valor do cabeçalho
Authorizationsegue o formatoacs <AccessKey ID>:<signature>. Verifique a saída deGetPOPAuthStr.