全部产品
Search
文档中心

Alibaba Cloud SDK:Sintaks Permintaan dan Metode Tanda Tangan V2 untuk API bergaya ROA

更新时间:Jul 02, 2025

Topik ini menjelaskan cara mengirim permintaan HTTP untuk memanggil operasi API Alibaba Cloud dalam arsitektur berorientasi sumber daya (ROA).

Penting

Sintaks permintaan dan metode tanda tangan V2 sudah tidak digunakan lagi. Gunakan sintaks permintaan dan metode tanda tangan V3.

Sintaks Permintaan HTTP

Tabel berikut menjelaskan parameter yang termasuk dalam permintaan API Alibaba Cloud yang lengkap.

Komponen

Diperlukan

Deskripsi

Contoh

Protokol

Ya

Protokol permintaan. Anda dapat melihat protokol yang didukung oleh API di Metadata API. Jika API mendukung baik HTTP maupun HTTPS, kami sarankan Anda menggunakan HTTPS untuk keamanan yang lebih tinggi.

https://

Titik akhir

Ya

Titik akhir API layanan Alibaba Cloud. Topik yang mencantumkan titik akhir setiap layanan Alibaba Cloud tersedia. Anda dapat melihat titik akhir suatu layanan di berbagai wilayah dalam topik-topik tersebut.

bailian.cn-beijing.aliyuncs.com

parameter_URI_sumberdaya

Ya

URL dari operasi. URL berisi jalur API dan parameter permintaan di komponen jalur dan kueri.

/{WorkspaceId}/datacenter/category

RequestHeader

Ya

Header permintaan. Dalam banyak kasus, informasi seperti nomor versi API, titik akhir, dan informasi autentikasi disertakan. Untuk informasi lebih lanjut, lihat bagian Parameter untuk RequestHeader dari topik ini.

Accept:application/json

Authorization:acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=

Content-MD5:LP54yxk8n7KqF1PPgbJizw==

Content-Type:application/json

Date:Rab, 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

Ya

Parameter spesifik operasi dalam badan permintaan. Untuk informasi lebih lanjut, kunjungi OpenAPI Explorer.

{"CategoryName":"test","CategoryType":"UNSTRUCTURED"}

HTTPMethod

Ya

Metode permintaan API ROA. Nilai valid: PUT, POST, GET, dan DELETE. Anda dapat melihat metode permintaan yang didukung oleh API di Referensi API.

POST

Parameter untuk RequestHeader

Tabel berikut menjelaskan parameter yang termasuk dalam permintaan API Alibaba Cloud yang lengkap.

Parameter

Tipe

Diperlukan

Deskripsi

Contoh

Accept

String

Tidak

Format respons. Jika Anda tidak menentukan parameter ini, respons akan dalam format XML. Untuk memanggil operasi API bergaya ROA, atur nilainya menjadi application/json.

application/json

Content-MD5

String

Tidak

Nilai hash MD5 128-bit yang dikodekan Base64 dari badan permintaan.

LP54yxk8n7KqF1PPgbJizw==

Date

String

Ya

Timestamp permintaan. Timestamp berlaku selama 15 menit. Anda harus menginisiasi permintaan dalam waktu 15 menit setelah timestamp dibuat. Tentukan waktu dalam GMT sesuai dengan HTTP 1.1. Contoh: Sel 9 Apr 2019 07:35:29 GMT.

Rab, 16 Apr 2025 03:44:46 GMT

Host

String

Ya

Titik akhir API layanan Alibaba Cloud. Untuk informasi lebih lanjut, lihat bagian Struktur permintaan HTTP dari topik ini.

bailian.cn-beijing.aliyuncs.com

x-acs-signature-method

String

Ya jika permintaan tidak anonim

Metode enkripsi string tanda tangan. Atur nilainya menjadi HMAC-SHA1.

HMAC-SHA1

x-acs-signature-nonce

String

Ya

Nomor unik dan acak yang digunakan untuk mencegah serangan pemutaran jaringan. Untuk mencegah serangan pemutaran, kami sarankan Anda menggunakan angka yang berbeda untuk setiap permintaan.

ef34aae7-7bd2-413d-a541-680cd2c48538

x-acs-signature-version

String

Tidak

Versi tanda tangan. Atur nilainya menjadi 1.0.

1.0

x-acs-version

String

Ya

Versi API. Anda dapat memperoleh versi API di OpenAPI Explorer atau di Metadata API.

2023-12-29

Authorization

String

Ya jika permintaan tidak anonim

Informasi autentikasi yang digunakan untuk memverifikasi validitas permintaan. Tentukan nilai dalam format AccessKeyId:Signature. Parameter AccessKeyId menentukan ID AccessKey yang diberikan kepada Anda oleh Alibaba Cloud. Anda dapat melihat ID AccessKey Anda di Konsol Resource Access Management (RAM). Untuk informasi tentang cara membuat pasangan AccessKey, lihat Mendapatkan Pasangan AccessKey.

Parameter Signature menentukan string tanda tangan dari permintaan. Untuk informasi lebih lanjut, lihat bagian Metode tanda tangan dari topik ini.

acs YourAccessKeyId:D9uFJAJgLL+dryjBfQK+YeqGtoY=

Metode tanda tangan

Anda harus menandatangani semua permintaan API HTTP dan HTTPS untuk memastikan keamanan. Saat permintaan mencapai gateway API Alibaba Cloud, gateway akan menghitung ulang tanda tangan berdasarkan parameter permintaan dan header permintaan, lalu membandingkan tanda tangan yang dihitung dengan tanda tangan dalam permintaan untuk memverifikasi identitas pemanggil API. Mekanisme ini memastikan integritas dan keamanan data. Bagian ini menjelaskan cara menghitung tanda tangan.

Catatan

Semua permintaan dan respons dikodekan dalam UTF-8.

Langkah 1: Buat header kanonisasi

Header kanonisasi adalah header HTTP non-standar. Header non-standar adalah header yang namanya diawali dengan x-acs- dalam permintaan. Untuk membuat header kanonisasi, ikuti langkah-langkah berikut:

  1. Konversikan nama header permintaan yang diawali dengan x-acs- dalam header permintaan menjadi huruf kecil dan urutkan secara leksikografis naik.

  2. Hapus spasi awal dan akhir dari nama dan nilai header permintaan.

  3. Tambahkan pembatas \n ke akhir setiap header, termasuk header terakhir, dan gabungkan semua header.

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 sumber daya kanonisasi

Sumber daya kanonisasi adalah deskripsi kanonisasi dari sumber daya yang ingin Anda akses. Ikuti langkah-langkah berikut untuk membuat sumber daya kanonisasi:

  1. Urutkan parameter dalam string kueri permintaan berdasarkan nama dalam urutan abjad dan gabungkan parameter menggunakan ampersand (&) untuk menghasilkan string kueri yang diurutkan.

  2. Gabungkan jalur sumber daya yang diminta dengan string kueri yang diurutkan menggunakan tanda tanya (?) untuk mendapatkan sumber daya kanonisasi. Jalur sumber daya adalah bagian antara titik akhir dan string kueri. Jalur tersebut mencakup garis miring maju (/) setelah titik akhir tetapi tidak termasuk tanda tanya (?) sebelum string kueri. Jika permintaan tidak berisi string kueri, gunakan jalur sumber daya sebagai sumber daya kanonisasi.

Contoh:

/llm-p2e4XXXXXXXXsvtn/datacenter/category

Langkah 3: Buat string-to-sign

Buat string-to-sign berdasarkan pseudocode berikut:

String stringToSign = 
    HTTPMethod + "\n" +
    Accept + "\n" +
    ContentMD5 + "\n" +
    ContentType + "\n" +
    Date + "\n" +
    CanonicalizedHeaders +
    CanonicalizedResource

Parameter

Deskripsi

HTTPMethod

Metode HTTP dalam huruf besar, seperti POST atau GET.

Accept

Nilai header Accept. Atur nilainya menjadi string kosong jika header ini tidak ada.

ContentMD5

Nilai header Content-MD5. Atur nilainya menjadi string kosong jika header ini tidak ada.

ContentType

Nilai header Content-Type. Atur nilainya menjadi string kosong jika header ini tidak ada.

Catatan

Tipe Multipurpose Internet Mail Extensions (MIME) dari badan permintaan.

Date

Nilai header Date.

CanonicalizedHeaders

Header kanonisasi yang diperoleh dari Langkah 1: Buat header kanonisasi dari topik ini.

CanonicalizedResource

Sumber daya kanonisasi yang diperoleh dari Langkah 2: Buat sumber daya kanonisasi dari topik ini.

Contoh:

POST
application/json
LP54yxk8n7KqF1PPgbJizw==
application/json
Rab, 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 string tanda tangan

Hitung nilai kode otentikasi pesan berbasis hash (HMAC) dari string-to-sign menggunakan algoritma HMAC-SHA1 dan kodekan nilai HMAC menjadi string tanda tangan berdasarkan aturan pengkodean Base64. Untuk informasi lebih lanjut tentang HMAC, lihat RFC 2104.

String signature = Base64(HMAC_SHA1(SigningKey, stringToSign))
Penting

Atur parameter SigningKey ke rahasia AccessKey Anda. Untuk informasi lebih lanjut, lihat Buat Pasangan AccessKey.

Contoh:

s8ZMF/eAIvvPJwehLLha0bVNFJ0=

Langkah 5: Tentukan header Authorization.

Setelah Anda memperoleh string tanda tangan, buat nilai header Authorization berdasarkan kode berikut:

String Authorization = "acs " + AccessKeyId + ":" + signature

Contoh:

acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=

Langkah 6: Tambahkan tanda tangan ke permintaan dankirim 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:Rab, 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"}

Contoh tanda tangan

Java

Catatan

Dalam contoh ini, lingkungan runtime JDK 1.8 digunakan. Sesuaikan parameter berdasarkan kebutuhan bisnis Anda.

Untuk menggunakan metode tanda tangan di Java, tambahkan dependensi Maven berikut ke file 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) {
        // Contoh 1: Kirim 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: Kirim 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: Kirim 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);*/

        // Buat nilai header Authorization.
        generateSignature(signatureRequest);
        // Gunakan HTTPClient untuk mengirim permintaan.
        callApi(signatureRequest);
    }

    private static void generateSignature(SignatureRequest signatureRequest) {
        try {
            // Langkah 1: Buat header kanonisasi
            String canonicalHeaders = buildCanonicalHeaders(signatureRequest);
            // Langkah 2: Buat sumber daya kanonisasi
            String canonicalQueryString = buildQueryString(signatureRequest);
            // Langkah 3: Buat string-to-sign
            String stringToSign = buildStringToSign(signatureRequest, canonicalQueryString, canonicalHeaders);
            // Langkah 4: Hitung string tanda tangan
            String signature = signString(stringToSign);
            // Langkah 5: Tentukan 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);
            // Tentukan 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");
            }
            // Tentukan 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 kesalahan.
                System.out.println("Gagal mengirim permintaan");
                throw new IllegalArgumentException(e.toString());
            }
        } catch (URISyntaxException e) {
            // Tangani kesalahan.
            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 informasi lebih lanjut tentang perbedaan antara API bergaya RPC dan API bergaya ROA, lihat Gaya API.