Configure Signature Authentication for HTTP Triggers

更新时间:
复制 MD 格式

Function Compute supports signature authentication for HTTP triggers. When a request reaches the Function Compute gateway, the gateway validates the request signature before invoking your function — your function code does not need to re-authenticate the request.

When to use signature authentication: Choose signature authentication if only callers with a valid Alibaba Cloud AccessKey should invoke the trigger. For publicly accessible triggers, leave authentication disabled and handle authorization in your function code instead.

Prerequisites

Before you begin, ensure that you have:

  • A Function Compute function with an HTTP trigger. To create one, see Create a Function

  • An Alibaba Cloud AccessKey ID and AccessKey Secret

Enable signature authentication for an HTTP trigger

  1. Log on to the Function Compute console. In the left-side navigation pane, click Functions.

  2. In the top navigation bar, select a region. On the Functions page, click the function you want to manage.

  3. On the function configuration page, click the Trigger tab. In the Actions column next to the target trigger, click Modify.

  4. In the Edit Trigger panel, set Authentication Method to Signature Authentication, then click OK.

Call an HTTP trigger with signature authentication

HTTP triggers use the Alibaba Cloud SDK signature mechanism (ACS3-HMAC-SHA256). For the full specification, see Signature Mechanism.

How it works

To call a signature-authenticated HTTP trigger:

  1. Generate a signature string using the Alibaba Cloud signing SDK.

  2. Set the signature string in the Authorization header of the HTTP request.

  3. Send the request with any HTTP client.

Required headers:

HeaderRequiredDescription
x-acs-dateYesCurrent UTC time in ISO 8601 format. Must be set before generating the signature.
x-acs-security-tokenWhen using Security Token Service (STS)Temporary security token from STS. Pass an empty string if not using STS.
authorizationYesThe signature string generated by the signing SDK. Set this after generating the signature.

Install the signing SDK

LanguageInstallation
Gogo get github.com/alibabacloud-go/openapi-util/service
Pythonpip install alibabacloud-openapi-util
Node.jsnpm install @alicloud/openapi-util
Java (Maven)See the dependency snippet below

Java Maven dependencies:

<dependency>
  <groupId>com.aliyun</groupId>
  <artifactId>openapiutil</artifactId>
  <version>0.2.1</version>
</dependency>
<dependency>
  <groupId>com.aliyun</groupId>
  <artifactId>tea-openapi</artifactId>
  <version>0.3.1</version>
</dependency>

Code examples

All examples below sign a POST request using ACS3-HMAC-SHA256 and set the result in the authorization header. Replace https://xx.cn-shanghai.fcapp.run/hello?foo=bar with your HTTP trigger address.

<details> <summary>Python</summary>

# -*- coding: utf-8 -*-

import os
from datetime import datetime
from urllib.parse import urlparse, parse_qs
import requests
from alibabacloud_openapi_util.client import Client as util
from Tea.request import TeaRequest

accessKeyId = os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID']
accessKeySecret = os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET']
securityToken = os.environ.get('ALIBABA_CLOUD_SECURITY_TOKEN', '')   # Optional. Required when using STS.

# The method parameter must be uppercase, such as POST or GET. If your request method is GET, change requests.post to requests.get below.
method = 'POST'
body = 'hello world'
url = 'https://xx.cn-shanghai.fcapp.run/hello?foo=bar'   # Your HTTP trigger address
date = datetime.utcnow().isoformat('T')[:19]+'Z'
headers = {
    'x-acs-date': date,
    'x-acs-security-token': securityToken
}

parsedUrl = urlparse(url)
authRequest = TeaRequest()
authRequest.method = method
authRequest.pathname = parsedUrl.path.replace('$', '%24')
authRequest.headers = headers
authRequest.query = {k: v[0] for k, v in parse_qs(parsedUrl.query).items()}

auth = util.get_authorization(authRequest, 'ACS3-HMAC-SHA256', '', accessKeyId, accessKeySecret)
headers['authorization'] = auth

# If the method is GET, change requests.post to requests.get below.
resp = requests.post(url, body, headers=headers)

print(resp.text)

</details>

<details> <summary>Java</summary>

package com.aliyun.sample;

import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;

import com.aliyun.tea.*;

public class Sample {
    public static void main(String[] args_) throws Exception {
        String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
        String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        String securityToken = System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN");  // Optional. Required when using STS.
        if (securityToken == null) {
            securityToken = "";
        }

        // The method parameter must be uppercase, such as POST or GET. If your request method is GET, change HttpPost to HttpGet below.
        String method = "POST";
        String body = "hello world";
        String url = "https://xx.cn-shanghai.fcapp.run/hello?foo=bar";  // Your HTTP trigger address
        Map<String, String> headers = new HashMap<String, String>();
        String date = Instant.now().toString();
        headers.put("x-acs-date", date);
        headers.put("x-acs-security-token", securityToken);

        URI uri = new URI(url);
        Map<String, String> query = new HashMap<String, String>();
        for (NameValuePair pair : URLEncodedUtils.parse(uri, StandardCharsets.UTF_8)) {
            query.put(pair.getName(), pair.getValue());
        }
        TeaRequest req = new TeaRequest();
        req.method = method;
        req.pathname = uri.getPath().replace("$", "%24");
        req.headers = headers;
        req.query = query;

        String auth = com.aliyun.openapiutil.Client.getAuthorization(
            req, "ACS3-HMAC-SHA256", "", accessKeyId, accessKeySecret);
        headers.put("authorization", auth);

        // If the method is GET, change HttpPost to HttpGet below.
        HttpPost request = new HttpPost(url);
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            request.setHeader(entry.getKey(), entry.getValue());
        }
        StringEntity entity = new StringEntity(body);
        request.setEntity(entity);

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            org.apache.http.HttpResponse response = httpClient.execute(request);
            String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            System.out.println(responseString);
        }
    }
}

</details>

<details> <summary>Go</summary>

package main

import (
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	openapiutil "github.com/alibabacloud-go/openapi-util/service"
	"github.com/alibabacloud-go/tea/tea"
)

func main() {
	accessKeyId := tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
	accessKeySecret := tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"))
	securityToken := tea.String(os.Getenv("ALIBABA_CLOUD_SECURITY_TOKEN")) // Optional. Required when using STS.

	// The method parameter must be uppercase.
	method := "POST"
	body := "hello world"
	url := "https://xx.cn-shanghai.fcapp.run/hello?foo=bar" // Your HTTP trigger address
	req, err := http.NewRequest(method, url, strings.NewReader(body))
	if err != nil {
		log.Printf("new request error: %v", err)
		return
	}
	date := time.Now().UTC().Format(time.RFC3339)
	req.Header.Set("x-acs-date", date)
	req.Header.Set("x-acs-security-token", *securityToken)

	authRequest := &tea.Request{
		Method:   &method,
		Pathname: tea.String(strings.ReplaceAll(req.URL.Path, "$", "%24")),
		Headers:  make(map[string]*string),
		Query:    make(map[string]*string),
	}
	for k := range req.URL.Query() {
		authRequest.Query[k] = tea.String(req.URL.Query().Get(k))
	}
	for k := range req.Header {
		authRequest.Headers[k] = tea.String(req.Header.Get(k))
	}
	auth := openapiutil.GetAuthorization(authRequest, tea.String("ACS3-HMAC-SHA256"), nil, accessKeyId, accessKeySecret)
	req.Header.Set("authorization", *auth)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Printf("post error: %v", err)
		return
	}
	defer resp.Body.Close()
	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("read body error: %v", err)
		return
	}
	log.Printf("resp: %v, body: %s", resp, string(buf))
}

</details>

<details> <summary>Node.js</summary>

const util = require("@alicloud/openapi-util");
const axios = require('axios');

async function main() {
    const accessKeyId = process.env['ALIBABA_CLOUD_ACCESS_KEY_ID'];
    const accessKeySecret = process.env['ALIBABA_CLOUD_ACCESS_KEY_SECRET'];
    const securityToken = process.env['ALIBABA_CLOUD_SECURITY_TOKEN'] || ''; // Optional. Required when using STS.

    // The method parameter must be uppercase, such as POST or GET. If your request method is GET, change axios.post to axios.get below.
    const method = 'POST';
    const body = 'hello world';
    const url = 'https://xx.cn-shanghai.fcapp.run/hello?foo=bar'; // Your HTTP trigger address
    const date = new Date().toISOString();
    let headers = {
        'x-acs-date': date,
        'x-acs-security-token': securityToken
    };

    const parsedUrl = new URL(url);
    const authRequest = {
        method: method,
        pathname: parsedUrl.pathname.replace('$', '%24'),
        headers: headers,
        query: Object.fromEntries(parsedUrl.searchParams),
    };
    const auth = util.default.getAuthorization(authRequest, 'ACS3-HMAC-SHA256', '', accessKeyId, accessKeySecret);
    headers['authorization'] = auth;

    // If the method is GET, change axios.post to axios.get below.
    const resp = await axios.post(url, body, {
        headers: headers,
    });
    console.log('resp: ', resp.data);
}

main().catch(console.error);

</details>

Verify the setup

This procedure uses Go to verify that signature authentication works end to end.

  1. Install the Go signing SDK:

    go get github.com/alibabacloud-go/openapi-util/service
  2. Save the Go example above as main.go. Update the url variable with your HTTP trigger address.

  3. Run the code:

    go run main.go

A successful response looks similar to:

2024/02/22 17:21:31 resp: &{200 OK 200 HTTP/1.1 1 1 map[Access-Control-Expose-Headers:[Date,x-fc-request-id] Content-Disposition:[attachment] Content-Length:[14] Content-Type:[text/plain; charset=utf-8] Date:[Thu, 22 Feb 2024 09:21:31 GMT] X-Fc-Request-Id:[1-65d71219-15d63510-fecf237c590c]] 0xc000120040 14 [] false false map[] 0xc000100100 0xc0000e0370}, body: Hello, Golang!

Troubleshooting

"required HTTP header Date was not specified"

Cause: The request has no valid signature, or the x-acs-date header is missing.

Resolution: Set the x-acs-date header to the current UTC time before calling the signing SDK, then add the returned signature to the authorization header.

"the difference between the request time '...' and the current time '...' is too large"

Cause: The signature has expired. Signatures are time-bound to prevent replay attacks.

Resolution: Re-sign the request using the current timestamp and retry immediately.

"The request signature we calculated does not match the signature you provided. Check your access key and signing method"

Cause: The signature in the request does not match the one Function Compute calculated.

Resolution: Check the following:

  • The AccessKey ID and AccessKey Secret are correct and belong to the same account.

  • The signing algorithm is ACS3-HMAC-SHA256.

  • The request method, path, headers, and query parameters passed to the signing SDK match the actual HTTP request sent.