Signez et envoyez des requêtes HTTP de style ROA vers Alibaba Cloud OpenAPI à l'aide du mécanisme de signature V2.
Cette méthode d'accès n'est plus recommandée. Utilisez plutôt le mécanisme de corps de requête et de signature V3.
Structure de la requête HTTP
Une requête OpenAPI complète se compose des éléments suivants.
|
Nom |
Obligatoire |
Description |
Exemple |
|
Protocole |
Oui |
Protocole de la requête. Consultez les métadonnées OpenAPI pour connaître les protocoles pris en charge. Privilégiez HTTPS lorsqu'il est disponible. |
https:// |
|
Endpoint |
Oui |
Endpoint du service. Reportez-vous à la documentation spécifique au produit pour obtenir l'endpoint correspondant. |
bailian.cn-beijing.aliyuncs.com |
|
resource_URI_parameters |
Oui |
URL de l'API, comprenant le chemin d'accès et les paramètres de requête. |
/{WorkspaceId}/datacenter/category |
|
RequestHeader |
Oui |
En-tête de la requête, incluant la version de l'API, l'hôte (Host) et l'autorisation (Authorization). Ces champs sont détaillés dans la section RequestHeader ci-dessous. |
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 |
Oui |
Corps de la requête contenant les paramètres métier. Testez vos requêtes sur le Portail développeur OpenAPI d'Alibaba Cloud. |
{"CategoryName":"test","CategoryType":"UNSTRUCTURED"} |
|
HTTPMethod |
Oui |
Méthode HTTP : PUT, POST, GET ou DELETE. La méthode requise est indiquée dans le document de l'API. |
POST |
RequestHeader
Le RequestHeader comprend les champs suivants.
|
Nom |
Type |
Obligatoire |
Description |
Exemple |
|
Accept |
String |
Non |
Format de réponse attendu. Le style ROA prend uniquement en charge |
application/json |
|
Content-MD5 |
String |
Non |
Hachage MD5 128 bits du RequestBody, encodé en Base64. |
LP54yxk8n7KqF1PPgbJizw== |
|
Date |
String |
Oui |
Horodatage actuel au format GMT HTTP 1.1. Valable pendant 15 minutes. |
Wed, 16 Apr 2025 03:44:46 GMT |
|
Host |
String |
Oui |
Endpoint du service. Cette valeur doit correspondre à l'endpoint indiqué dans la section Structure de la requête HTTP. |
bailian.cn-beijing.aliyuncs.com |
|
x-acs-signature-method |
String |
Requis pour les requêtes non anonymes |
Algorithme de signature. Valeur fixe : |
HMAC-SHA1 |
|
x-acs-signature-nonce |
String |
Oui |
Nombre aléatoire unique destiné à prévenir les attaques par rejeu. Cette valeur doit être différente pour chaque requête. |
ef34aae7-7bd2-413d-a541-680cd2c48538 |
|
x-acs-signature-version |
String |
Non |
Version de la signature. Valeur fixe : |
1.0 |
|
x-acs-version |
String |
Oui |
Version de l'API. Vous trouverez cette information sur le Portail développeur OpenAPI d'Alibaba Cloud ou dans les métadonnées OpenAPI. |
2023-12-29 |
|
Authorization |
String |
Requis pour les requêtes non anonymes |
Chaîne d'authentification au format La signature correspond à la signature de la requête, calculée selon le mécanisme de signature. |
acs YourAccessKeyId:D9uFJAJgLL+dryjBfQK+YeqGtoY= |
Mécanisme de signature
Toute requête doit comporter une signature à des fins d'authentification. La passerelle API d'Alibaba Cloud recalcule cette signature à partir des paramètres et des en-têtes de la requête, puis la compare à la signature soumise afin de vérifier l'identité de l'appelant et l'intégrité des données. Procédez comme suit pour calculer la signature :
Les requêtes et les réponses utilisent l'encodage UTF-8.
Étape 1 : Construire les en-têtes canoniques
Les en-têtes canoniques (CanonicalizedHeaders) correspondent aux en-têtes HTTP non standard préfixés par x-acs-. Construisez-les en suivant ces étapes :
Convertissez en minuscules les noms des en-têtes
x-acs-issus du RequestHeader, puis triez-les par ordre lexicographique.Supprimez les espaces superflus au début et à la fin des noms et des valeurs des en-têtes.
Ajoutez
\naprès chaque en-tête (y compris le dernier), puis concaténez l'ensemble pour former les CanonicalizedHeaders.
Exemple :
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
Étape 2 : Construire la ressource canonique
Construisez la ressource canonique (CanonicalizedResource) comme suit :
Triez les paramètres de la chaîne de requête par nom selon l'ordre lexicographique, puis assemblez-les avec le caractère
&.Accolez la chaîne de requête triée au chemin de la ressource en utilisant le séparateur
?. Le chemin de la ressource correspond au segment de l'URL situé entre l'hôte et la chaîne de requête ; il commence toujours par/. En l'absence de chaîne de requête, utilisez uniquement le chemin de la ressource.
Exemple :
/llm-p2e4XXXXXXXXsvtn/datacenter/category
Étape 3 : Construire la chaîne à signer
Élaborez la chaîne à signer (stringToSign) en vous basant sur ce pseudo-code :
String stringToSign =
HTTPMethod + "\n" +
Accept + "\n" +
ContentMD5 + "\n" +
ContentType + "\n" +
Date + "\n" +
CanonicalizedHeaders +
CanonicalizedResource
|
Paramètre |
Description |
|
HTTPMethod |
Méthode HTTP en majuscules, telle que POST ou GET. |
|
Accept |
Valeur de l'en-tête Accept. Chaîne vide si cet en-tête est absent. |
|
ContentMD5 |
Valeur de l'en-tête Content-MD5. Chaîne vide si cet en-tête est absent. |
|
ContentType |
Valeur de l'en-tête Content-Type. Chaîne vide si cet en-tête est absent. Remarque
Type MIME du RequestBody. |
|
Date |
Valeur de l'en-tête Date. |
|
CanonicalizedHeaders |
En-têtes canoniques obtenus lors de l'Étape 1 : Construire les en-têtes canoniques. |
|
CanonicalizedResource |
Ressource canonique obtenue lors de l'Étape 2 : Construire la ressource canonique. |
Exemple :
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
Étape 4 : Calculer la signature
Conformément à la norme RFC2104, signez la chaîne issue de l'étape 3 à l'aide de l'algorithme HMAC-SHA1, puis encodez le résultat en Base64.
String signature = Base64(HMAC_SHA1(SigningKey, stringToSign))
SigningKey correspond à votre secret AccessKey. Créez une AccessKey si vous n'en possédez pas encore.
Exemple :
s8ZMF/eAIvvPJwehLLha0bVNFJ0=
Étape 5 : Construire l'en-tête Authorization
Attribuez à l'en-tête Authorization la valeur suivante.
String Authorization = "acs " + AccessKeyId + ":" + signature
Exemple de valeur :
acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=
Étape 6 : Ajouter la signature à la requête et envoyer celle-ci
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"}
Exemples de code pour la signature
Exemple Java
Ce code s'exécute sur JDK 1.8. Adaptez-le à votre environnement si nécessaire.
Ajoutez les dépendances Maven suivantes à votre fichier 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());
}
}
}