Gunakan metode penandatanganan mandiri bergaya ROA untuk mengirim permintaan HTTP ke Alibaba Cloud OpenAPI.
Metode akses ini tidak lagi direkomendasikan. Gunakan mekanisme badan permintaan dan signature V3 sebagai gantinya.
Struktur permintaan HTTP
Permintaan OpenAPI lengkap mencakup bagian-bagian berikut.
|
Name |
Required |
Description |
Example |
|
Protocol |
Yes |
Protokol permintaan. Periksa metadata OpenAPI untuk protokol yang didukung. Jika |
https:// |
|
Endpoint |
Yes |
Titik akhir layanan. Temukan titik akhir spesifik produk dalam dokumentasi titik akhir yang sesuai. |
bailian.cn-beijing.aliyuncs.com |
|
resource_URI_parameters |
Yes |
URL API, termasuk path dan parameter kueri. |
/{WorkspaceId}/datacenter/category |
|
RequestHeader |
Yes |
Header permintaan, termasuk versi API, Host, dan Authorization. Lihat bagian RequestHeader di bawah ini. |
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 |
Yes |
Badan permintaan yang berisi parameter bisnis. Coba permintaan Anda di Alibaba Cloud OpenAPI Developer Portal. |
{"CategoryName":"test","CategoryType":"UNSTRUCTURED"} |
|
HTTPMethod |
Yes |
Metode HTTP: PUT, POST, GET, atau DELETE. Periksa dokumentasi API untuk metode yang diperlukan. |
POST |
RequestHeader
RequestHeader mencakup bidang-bidang berikut.
|
Name |
Type |
Required |
Description |
Example |
|
Accept |
String |
No |
Format respons. ROA hanya mendukung |
application/json |
|
Content-MD5 |
String |
No |
Hash MD5 128-bit dari RequestBody, dikonversi menjadi string terenkripsi Base64. |
LP54yxk8n7KqF1PPgbJizw== |
|
Date |
String |
Yes |
Timestamp saat ini dalam format GMT HTTP 1.1. Berlaku selama 15 menit. Contoh: Tue 9 Apr 2019 07:35:29 GMT. |
Wed, 16 Apr 2025 03:44:46 GMT |
|
Host |
String |
Yes |
Titik akhir layanan. Lihat bidang endpoint di struktur permintaan HTTP. |
bailian.cn-beijing.aliyuncs.com |
|
x-acs-signature-method |
String |
Required for non-anonymous requests |
Algoritma penandatanganan. Nilai tetap: |
HMAC-SHA1 |
|
x-acs-signature-nonce |
String |
Yes |
Bilangan acak unik untuk mencegah serangan replay. Gunakan nilai berbeda untuk setiap permintaan. |
ef34aae7-7bd2-413d-a541-680cd2c48538 |
|
x-acs-signature-version |
String |
No |
Versi signature. Nilai tetap: |
1.0 |
|
x-acs-version |
String |
Yes |
Versi API. Temukan versi tersebut di Alibaba Cloud OpenAPI Developer Portal atau metadata OpenAPI. |
2023-12-29 |
|
Authorization |
String |
Required for non-anonymous requests |
String otentikasi dalam format Signature adalah signature permintaan. Untuk nilainya, lihat Mekanisme signature. |
acs YourAccessKeyId:D9uFJAJgLL+dryjBfQK+YeqGtoY= |
Mekanisme signature
Semua permintaan HTTP dan HTTPS harus menyertakan signature untuk otentikasi. Alibaba Cloud API Gateway menghitung ulang signature dari parameter dan header permintaan, lalu membandingkannya dengan signature dalam permintaan untuk memverifikasi identitas pemohon dan integritas data. Perhitungan signature mengikuti langkah-langkah berikut:
Permintaan dan respons dienkoding dalam set karakter UTF-8.
Langkah 1: Buat header terkanonisasi
Header terkanonisasi (CanonicalizedHeaders) adalah header HTTP non-standar yang diawali dengan x-acs-. Buat seperti berikut:
-
Ubah nama header di RequestHeader yang dimulai dengan
x-acs-menjadi huruf kecil dan urutkan secara leksikografis menaik. -
Hapus spasi awal dan akhir dari nama dan nilai header.
-
Tambahkan
\nsetelah setiap header (termasuk header terakhir), lalu gabungkan semua header untuk membentuk CanonicalizedHeaders.
Contoh:
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
Langkah 2: Buat resource terkanonisasi
Resource terkanonisasi (CanonicalizedResource) menggambarkan resource target. Buat seperti berikut:
-
Urutkan parameter string kueri berdasarkan nama secara leksikografis dan gabungkan dengan
&. -
Tambahkan string kueri yang telah diurutkan ke path resource menggunakan
?sebagai pemisah. Path resource adalah segmen URL antara host dan string kueri, dimulai dengan/. Jika tidak ada string kueri, gunakan hanya path resource.
Contoh:
/llm-p2e4XXXXXXXXsvtn/datacenter/category
Langkah 3: Buat string yang akan ditandatangani
Buat string yang akan ditandatangani (stringToSign) menggunakan pseudokode berikut:
String stringToSign =
HTTPMethod + "\n" +
Accept + "\n" +
ContentMD5 + "\n" +
ContentType + "\n" +
Date + "\n" +
CanonicalizedHeaders +
CanonicalizedResource
|
Parameter |
Description |
|
HTTPMethod |
Nama metode HTTP dalam huruf kapital, seperti POST atau GET. |
|
Accept |
Nilai header Accept. Gunakan string kosong jika tidak ada. |
|
ContentMD5 |
Nilai header Content-MD5. Gunakan string kosong jika tidak ada. |
|
ContentType |
Nilai header Content-Type. Gunakan string kosong jika tidak ada. Catatan
Jenis MIME dari RequestBody. |
|
Date |
Nilai header Date. |
|
CanonicalizedHeaders |
Header terkanonisasi yang diperoleh pada Langkah 1: Buat header terkanonisasi. |
|
CanonicalizedResource |
Resource terkanonisasi yang diperoleh pada Langkah 2: Buat resource terkanonisasi. |
Contoh:
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
Langkah 4: Hitung signature
Seperti yang didefinisikan dalam RFC2104, tandatangani string dari langkah sebelumnya menggunakan HMAC-SHA1, lalu enkode hasilnya dengan Base64.
String signature = Base64(HMAC_SHA1(SigningKey, stringToSign))
SigningKey adalah rahasia AccessKey Anda. Buat AccessKey.
Contoh:
s8ZMF/eAIvvPJwehLLha0bVNFJ0=
Langkah 5: Buat header Authorization
Gabungkan string-string berikut dan tetapkan hasilnya sebagai nilai header Authorization.
String Authorization = "acs " + AccessKeyId + ":" + signature
Nilai contoh:
acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=
Langkah 6: Tambahkan signature ke permintaan dan kirim permintaan
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"}
Kode contoh signature
Contoh Java
Kode contoh dijalankan pada JDK 1.8. Sesuaikan sesuai kebutuhan lingkungan Anda.
Tambahkan dependensi Maven berikut ke file pom.xml Anda.
<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) {
// Contoh 1: Permintaan POST
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);
/*// Contoh 2: Permintaan GET
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");*/
/*// Contoh 3: Permintaan DELETE
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);*/
// Hasilkan Authorization
generateSignature(signatureRequest);
// Kirim permintaan menggunakan HTTPClient
callApi(signatureRequest);
}
private static void generateSignature(SignatureRequest signatureRequest) {
try {
// Langkah 1: Buat header terkanonisasi
String canonicalHeaders = buildCanonicalHeaders(signatureRequest);
// Langkah 2: Buat resource terkanonisasi
String canonicalQueryString = buildQueryString(signatureRequest);
// Langkah 3: Buat string yang akan ditandatangani
String stringToSign = buildStringToSign(signatureRequest, canonicalQueryString, canonicalHeaders);
// Langkah 4: Buat string signature
String signature = signString(stringToSign);
// Langkah 5: Buat header Authorization
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 {
// Kirim permintaan menggunakan HttpClient
String url = "https://" + signatureRequest.host + signatureRequest.canonicalUri;
URIBuilder uriBuilder = new URIBuilder(url);
// Tambahkan parameter permintaan
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("Metode HTTP tidak didukung: " + signatureRequest.httpMethod);
throw new IllegalArgumentException("Metode HTTP tidak didukung");
}
// Tambahkan header permintaan HTTP
signatureRequest.headers.forEach(httpRequest::addHeader);
// Kirim permintaan
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) {
// Tangani pengecualian
System.out.println("Gagal mengirim permintaan");
throw new IllegalArgumentException(e.toString());
}
} catch (URISyntaxException e) {
// Tangani pengecualian
System.out.println("Sintaks URI tidak valid");
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());
}
}
}
Referensi
Untuk memahami perbedaan antara dua gaya API tersebut, lihat Membedakan gaya ROA dan RPC.