Assine e envie requisições HTTP estilo ROA para a Alibaba Cloud OpenAPI com o mecanismo de assinatura V2.
Este método de acesso não é mais recomendado. Utilize o Corpo da requisição e mecanismo de assinatura V3.
Estrutura da requisição HTTP
Uma requisição completa à OpenAPI inclui as partes a seguir.
|
Nome |
Obrigatório |
Descrição |
Exemplo |
|
Protocolo |
Sim |
Protocolo da requisição. Consulte os metadados da OpenAPI para verifique os protocolos suportados. Prefira HTTPS quando disponível. |
https:// |
|
Endpoint |
Sim |
Endpoint do serviço. Verifique a documentação específica do produto para obter o endpoint correto. |
bailian.cn-beijing.aliyuncs.com |
|
resource_URI_parameters |
Sim |
URL da API, incluindo caminho e parâmetros de consulta. |
/{WorkspaceId}/datacenter/category |
|
RequestHeader |
Sim |
Cabeçalho da requisição com versão da API, Host e Authorization. Definição na seção RequestHeader abaixo. |
Accept:application/json Authorization:acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0= Content-MD5:LP54yxk8n7KqF1PPgbJizw== Content-Type:application/json Date:Wed, 16 Apr 2025 03:44:46 GMT Host:bailian.cn-beijing.aliyuncs.com x-acs-signature-method:HMAC-SHA1 x-acs-signature-nonce:ef34aae7-7bd2-413d-a541-680cd2c48538 x-acs-signature-version:1.0 x-acs-version:2023-12-29 |
|
RequestBody |
Sim |
Corpo da requisição com parâmetros de negócio. Teste requisições no Portal do Desenvolvedor da Alibaba Cloud OpenAPI. |
{"CategoryName":"test","CategoryType":"UNSTRUCTURED"} |
|
HTTPMethod |
Sim |
Método HTTP: PUT, POST, GET ou DELETE. O método necessário consta no documento da API. |
POST |
RequestHeader
O RequestHeader contém os campos a seguir.
|
Nome |
Tipo |
Obrigatório |
Descrição |
Exemplo |
|
Accept |
String |
Não |
Formato da resposta. O estilo ROA suporta apenas |
application/json |
|
Content-MD5 |
String |
Não |
Hash MD5 de 128 bits do RequestBody codificado em Base64. |
LP54yxk8n7KqF1PPgbJizw== |
|
Date |
String |
Sim |
Timestamp atual no formato GMT do HTTP 1.1. Validade de 15 minutos. |
Wed, 16 Apr 2025 03:44:46 GMT |
|
Host |
String |
Sim |
Endpoint do serviço. Deve corresponder ao endpoint indicado na Estrutura da requisição HTTP. |
bailian.cn-beijing.aliyuncs.com |
|
x-acs-signature-method |
String |
Obrigatório para requisições não anônimas |
Algoritmo de assinatura. Valor fixo: |
HMAC-SHA1 |
|
x-acs-signature-nonce |
String |
Sim |
Número aleatório único para evitar ataques de replay. Diferente em cada requisição. |
ef34aae7-7bd2-413d-a541-680cd2c48538 |
|
x-acs-signature-version |
String |
Não |
Versão da assinatura. Valor fixo: |
1.0 |
|
x-acs-version |
String |
Sim |
Versão da API. Consulte a versão no Portal do Desenvolvedor da Alibaba Cloud OpenAPI ou nos metadados da OpenAPI. |
2023-12-29 |
|
Authorization |
String |
Obrigatório para requisições não anônimas |
String de autenticação no formato Signature é a assinatura da requisição, calculada conforme o Mecanismo de assinatura. |
acs YourAccessKeyId:D9uFJAJgLL+dryjBfQK+YeqGtoY= |
Mecanismo de assinatura
Todas as requisições exigem assinatura para autenticação. O API Gateway da Alibaba Cloud recalcula a assinatura com base nos parâmetros e cabeçalhos da requisição e a compara com a assinatura enviada para validar identidade e integridade dos dados. Calcule a assinatura seguindo estas etapas:
Requisições e respostas usam codificação UTF-8.
Etapa 1: Construir os cabeçalhos canônicos
Os cabeçalhos canônicos (CanonicalizedHeaders) são cabeçalhos HTTP não padrão com prefixo x-acs-. Construa-os assim:
Converta os nomes dos cabeçalhos
x-acs-do RequestHeader para minúsculas e ordene-os lexicograficamente.Remova espaços à esquerda e à direita dos nomes e valores dos cabeçalhos.
Adicione
\napós cada cabeçalho (inclusive o último) e concatene todos para formar os CanonicalizedHeaders.
Exemplo:
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:ef34aae7-7bd2-413d-a541-680cd2c48538
x-acs-signature-version:1.0
x-acs-version:2023-12-29
Etapa 2: Construir o recurso canônico
Construa o recurso canônico (CanonicalizedResource) conforme descrito abaixo:
Ordene os parâmetros da string de consulta por nome, em ordem lexicográfica, e una-os com
&.Anexe a string de consulta ordenada ao caminho do recurso usando
?. O caminho do recurso é o segmento da URL entre o host e a string de consulta, começando com/. Se não houver string de consulta, use apenas o caminho do recurso.
Exemplo:
/llm-p2e4XXXXXXXXsvtn/datacenter/category
Etapa 3: Construir a string a ser assinada
Monte a string a ser assinada (stringToSign) com este pseudocódigo:
String stringToSign =
HTTPMethod + "\n" +
Accept + "\n" +
ContentMD5 + "\n" +
ContentType + "\n" +
Date + "\n" +
CanonicalizedHeaders +
CanonicalizedResource
|
Parâmetro |
Descrição |
|
HTTPMethod |
Método HTTP em maiúsculas, como POST ou GET. |
|
Accept |
Valor do cabeçalho Accept. String vazia se ausente. |
|
ContentMD5 |
Valor do cabeçalho Content-MD5. String vazia se ausente. |
|
ContentType |
Valor do cabeçalho Content-Type. String vazia se ausente. Nota
Tipo MIME do RequestBody. |
|
Date |
Valor do cabeçalho Date. |
|
CanonicalizedHeaders |
Cabeçalhos canônicos da Etapa 1: Construir os cabeçalhos canônicos. |
|
CanonicalizedResource |
Recurso canônico da Etapa 2: Construir o recurso canônico. |
Exemplo:
POST
application/json
LP54yxk8n7KqF1PPgbJizw==
application/json
Wed, 16 Apr 2025 03:44:46 GMT
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:ef34aae7-7bd2-413d-a541-680cd2c48538
x-acs-signature-version:1.0
x-acs-version:2023-12-29
/llm-p2e4XXXXXXXXsvtn/datacenter/category
Etapa 4: Calcular a assinatura
Conforme a RFC2104, assine a string da Etapa 3 com HMAC-SHA1 e codifique o resultado em Base64.
String signature = Base64(HMAC_SHA1(SigningKey, stringToSign))
SigningKey corresponde ao segredo da sua AccessKey. Crie uma AccessKey.
Exemplo:
s8ZMF/eAIvvPJwehLLha0bVNFJ0=
Etapa 5: Construir o cabeçalho Authorization
Defina o cabeçalho Authorization com o valor a seguir.
String Authorization = "acs " + AccessKeyId + ":" + signature
Exemplo de valor:
acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=
Etapa 6: Adicionar a assinatura à requisição e enviá-la
POST https://bailian.cn-beijing.aliyuncs.com/llm-p2e4XXXXXXXXsvtn/datacenter/category HTTP/1.1
Accept:application/json
Authorization:acs YourAccessKeyId:r8Y9ZqVhTrYGl4nieqk7CW0Pwow=
Content-MD5:LP54yxk8n7KqF1PPgbJizw==
Content-Type:application/json
Date:Wed, 16 Apr 2025 06:47:10 GMT
Host:bailian.cn-beijing.aliyuncs.com
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:e3d8efa7-b1d8-42f3-9733-4fe2691e15dc
x-acs-signature-version:1.0
x-acs-version:2023-12-29
{"CategoryName":"test","CategoryType":"UNSTRUCTURED"}
Código de exemplo de assinatura
Exemplo em Java
Executa no JDK 1.8. Ajuste conforme seu ambiente.
Adicione estas dependências Maven ao arquivo pom.xml.
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
</dependency>
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
public class SignatureDemo {
public static class SignatureRequest {
private final String httpMethod;
private final String host;
private final String version;
private final String canonicalUri;
public TreeMap<String, String> headers = new TreeMap<>();
public TreeMap<String, Object> queryParams = new TreeMap<>();
public byte[] bodyByteArray;
public SignatureRequest(String httpMethod, String host, String version, String canonicalUri) {
this.httpMethod = httpMethod;
this.host = host;
this.version = version;
this.canonicalUri = canonicalUri;
initHeaders();
}
private void initHeaders() {
headers.put("Host", host);
headers.put("x-acs-version", version);
headers.put("x-acs-signature-version", "1.0");
headers.put("Accept", "application/json");
headers.put("x-acs-signature-nonce", java.util.UUID.randomUUID().toString());
headers.put("Date", getGmtDate());
headers.put("x-acs-signature-method", "HMAC-SHA1");
}
private String getGmtDate() {
SimpleDateFormat gmtFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.ENGLISH);
gmtFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
return gmtFormat.format(new Date());
}
public void setBody(Map<String, String> body, ContentType contentType) {
Gson gson = (new GsonBuilder()).disableHtmlEscaping().create();
this.bodyByteArray = gson.toJson(body).getBytes(StandardCharsets.UTF_8);
headers.put("Content-MD5", md5Sum(this.bodyByteArray));
headers.put("Content-Type", contentType.getMimeType());
}
}
private static final String ACCESS_KEY_ID = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
private static final String ACCESS_KEY_SECRET = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
private static final String ALGORITHM_NAME = "HmacSHA1";
public static void main(String[] args) {
// Example 1: POST request
String method = "POST";
String host = "bailian.cn-beijing.aliyuncs.com";
String version = "2023-12-29";
String canonicalUri = "/llm-p2e4XXXXXXXXsvtn/datacenter/category";
SignatureRequest signatureRequest = new SignatureRequest(method, host, version, canonicalUri);
Map<String, String> body = new HashMap<>();
body.put("CategoryName", "test");
body.put("CategoryType", "UNSTRUCTURED");
System.out.println(new Gson().toJson(body));
signatureRequest.setBody(body, ContentType.APPLICATION_JSON);
/*// Example 2: GET request
String method = "GET";
String host = "bailian.cn-beijing.aliyuncs.com";
String version = "2023-12-29";
String canonicalUri = "/llm-p2e4XXXXXXXXsvtn/datacenter/files";
SignatureRequest signatureRequest = new SignatureRequest(method, host, version, canonicalUri);
signatureRequest.queryParams.put("CategoryId", "cate_a946*********************_10045991");*/
/*// Example 3: DELETE request
String method = "DELETE";
String host = "bailian.cn-beijing.aliyuncs.com";
String version = "2023-12-29";
String canonicalUri = "/llm-p2e4XXXXXXXXsvtn/datacenter/category/cate_a946*********************_10045991";
SignatureRequest signatureRequest = new SignatureRequest(method, host, version, canonicalUri);*/
// Generate Authorization
generateSignature(signatureRequest);
// Send the request using HTTPClient
callApi(signatureRequest);
}
private static void generateSignature(SignatureRequest signatureRequest) {
try {
// Step 1: Construct the canonicalized headers
String canonicalHeaders = buildCanonicalHeaders(signatureRequest);
// Step 2: Construct the canonicalized resource
String canonicalQueryString = buildQueryString(signatureRequest);
// Step 3: Construct the string to sign
String stringToSign = buildStringToSign(signatureRequest, canonicalQueryString, canonicalHeaders);
// Step 4: Construct the signature string
String signature = signString(stringToSign);
// Step 5: Construct the Authorization header
String authorization = "acs " + ACCESS_KEY_ID + ":" + signature;
signatureRequest.headers.put("Authorization", authorization);
} catch (Exception ex) {
throw new IllegalArgumentException(ex.toString());
}
}
private static void callApi(SignatureRequest signatureRequest) {
try {
// Send the request using HttpClient
String url = "https://" + signatureRequest.host + signatureRequest.canonicalUri;
URIBuilder uriBuilder = new URIBuilder(url);
// Add request parameters
signatureRequest.queryParams.forEach((key, value) -> uriBuilder.addParameter(key, String.valueOf(value)));
HttpUriRequest httpRequest;
switch (signatureRequest.httpMethod) {
case "GET":
httpRequest = new HttpGet(uriBuilder.build());
break;
case "POST":
HttpPost httpPost = new HttpPost(uriBuilder.build());
if (signatureRequest.bodyByteArray != null) {
httpPost.setEntity(new ByteArrayEntity(signatureRequest.bodyByteArray, ContentType.create(signatureRequest.headers.get("Content-Type"))));
}
httpRequest = httpPost;
break;
case "PUT":
HttpPut httpPut = new HttpPut(uriBuilder.build());
if (signatureRequest.bodyByteArray != null) {
httpPut.setEntity(new ByteArrayEntity(signatureRequest.bodyByteArray, ContentType.create(signatureRequest.headers.get("Content-Type"))));
}
httpRequest = httpPut;
break;
case "DELETE":
httpRequest = new HttpDelete(uriBuilder.build());
break;
default:
System.out.println("Unsupported HTTP method: " + signatureRequest.httpMethod);
throw new IllegalArgumentException("Unsupported HTTP method");
}
// Add HTTP request headers
signatureRequest.headers.forEach(httpRequest::addHeader);
// Send the request
try (CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = httpClient.execute(httpRequest)) {
String result = EntityUtils.toString(response.getEntity(), "UTF-8");
System.out.println(result);
} catch (IOException e) {
// Handle exceptions
System.out.println("Failed to send request");
throw new IllegalArgumentException(e.toString());
}
} catch (URISyntaxException e) {
// Handle exceptions
System.out.println("Invalid URI syntax");
throw new IllegalArgumentException(e.toString());
}
}
private static String buildCanonicalHeaders(SignatureRequest signatureRequest) {
StringBuilder canonicalHeaders = new StringBuilder();
signatureRequest.headers.entrySet().stream()
.filter(entry -> entry.getKey().startsWith("x-acs-"))
.forEach(entry -> canonicalHeaders.append(entry.getKey().toLowerCase().trim()).append(":").append(entry.getValue().trim()).append("\n"));
return canonicalHeaders.toString();
}
private static String buildQueryString(SignatureRequest signatureRequest) {
StringBuilder queryBuilder = new StringBuilder(signatureRequest.canonicalUri);
if (!signatureRequest.queryParams.isEmpty()) {
queryBuilder.append("?");
}
for (Map.Entry<String, Object> entry : signatureRequest.queryParams.entrySet()) {
queryBuilder.append(entry.getKey());
String value = (String) entry.getValue();
if (value != null && !value.isEmpty()) {
queryBuilder.append("=").append(value).append("&");
} else {
queryBuilder.append("&");
}
}
String queryString = queryBuilder.toString();
if (queryString.endsWith("&")) {
queryString = queryString.substring(0, queryString.length() - 1);
}
return queryString;
}
private static String buildStringToSign(SignatureRequest signatureRequest, String canonicalQueryString, String canonicalHeaders) {
StringBuilder sb = new StringBuilder();
sb.append(signatureRequest.httpMethod).append("\n");
appendIfPresent(sb, signatureRequest.headers.get("Accept"));
appendIfPresent(sb, signatureRequest.headers.get("Content-MD5"));
appendIfPresent(sb, signatureRequest.headers.get("Content-Type"));
appendIfPresent(sb, signatureRequest.headers.get("Date"));
sb.append(canonicalHeaders);
sb.append(canonicalQueryString);
return sb.toString();
}
private static void appendIfPresent(StringBuilder sb, String value) {
if (value != null) {
sb.append(value).append("\n");
} else {
sb.append("\n");
}
}
private static String signString(String stringToSign) {
try {
Mac mac = Mac.getInstance(ALGORITHM_NAME);
mac.init(new SecretKeySpec(ACCESS_KEY_SECRET.getBytes(StandardCharsets.UTF_8), ALGORITHM_NAME));
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return DatatypeConverter.printBase64Binary(signData);
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
throw new IllegalArgumentException(ex.toString());
}
}
public static String md5Sum(byte[] buff) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] md5Bytes = md.digest(buff);
return DatatypeConverter.printBase64Binary(md5Bytes);
} catch (NoSuchAlgorithmException ex) {
throw new IllegalArgumentException(ex.toString());
}
}
}
Este exemplo crie um gatilho de aplicação com a operação Container Service for Kubernetes (CS) CreateTrigger. Considere AccessKey ID como testid, segredo da AccessKey como testsecret, x-acs-signature-nonce como 15215528852396 e Data como Tue 9 Apr 2022 07:35:29 GMT. Etapas do cálculo da assinatura:
Construa os cabeçalhos canônicos.
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:15215528852396
x-acs-signature-version:1.0
x-acs-version:2015-12-15
Construa o recurso canônico.
/clusters/test_cluster_id/triggers
Construa a string a ser assinada.
POST
application/json
Gtl/0jNYHf8t9Lq8Xlpaqw==
application/json
Tue 9 Apr 2022 07:35:29 GMT
x-acs-signature-method:HMAC-SHA1
x-acs-signature-nonce:15215528852396
x-acs-signature-version:1.0
x-acs-version:2015-12-15
/clusters/test_cluster_id/triggers
Calcule a assinatura.
acs testid:D9uFJAJgLL+dryjBfQK+YeqGtoY=
Adicione a assinatura à requisição.
POST /clusters/test_cluster_id/triggers HTTP/1.1
/* headers */
{
"accept": "application/json",
"date": "Tue 9 Apr 2022 07:35:29 GMT",
"host": "cs.cn-hangzhou.aliyuncs.com",
"x-acs-signature-nonce": "15215528852396",
"x-acs-signature-method": "HMAC-SHA1",
"x-acs-signature-version": "1.0",
"x-acs-version": "2015-12-15",
"content-type": "application/json",
"content-md5": "Gtl/0jNYHf8t9Lq8Xlpaqw==",
"authorization": "acs testid:D9uFJAJgLL+dryjBfQK+YeqGtoY="
}
/* body */
{
"project_id": "default/nginx-test",
"cluster_id": "test_cluster_id",
"action": "redeploy",
"type": "deployment"
}