すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud SDK:ROA 形式の API のリクエスト構文と署名方式 V2

最終更新日:May 10, 2025

このトピックでは、リソース指向アーキテクチャ(ROA)形式で Alibaba Cloud API オペレーションを呼び出すための HTTP リクエストを送信する方法について説明します。

重要

リクエスト構文と署名方式 V2 は廃止されました。リクエスト構文と署名方式 V3 を使用してください。

HTTP リクエスト構文

次の表は、完全な Alibaba Cloud API リクエストに含まれるパラメーターを示しています。

コンポーネント

必須

説明

プロトコル

はい

リクエストプロトコル。API でサポートされているプロトコルは、API メタデータで確認できます。API が HTTPHTTPS の両方をサポートしている場合は、セキュリティを高めるために HTTPS を使用することをお勧めします。

https://

エンドポイント

はい

Alibaba Cloud サービス API のエンドポイント。各 Alibaba Cloud サービスのエンドポイントをリストしたトピックがあります。これらのトピックでは、さまざまなリージョンにおけるサービスのエンドポイントを確認できます。

bailian.cn-beijing.aliyuncs.com

resource_URI_parameters

はい

操作の URL。URL には、API のパスと、パスおよびクエリコンポーネント内のリクエストパラメーターが含まれます。

/{WorkspaceId}/datacenter/category

RequestHeader

はい

リクエストヘッダー。ほとんどの場合、API バージョン番号、エンドポイント、認証情報などの情報が含まれます。詳細については、このトピックのRequestHeader のパラメーターセクションをご参照ください。

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

はい

リクエストボディ内の操作固有のパラメーター。詳細については、OpenAPI Explorer をご参照ください。

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

HTTPMethod

はい

ROA API のリクエストメソッド。有効な値:PUT、POST、GET、DELETE。API でサポートされているリクエストメソッドは、API リファレンスで確認できます。

POST

RequestHeader のパラメーター

次の表は、完全な Alibaba Cloud API リクエストに含まれるパラメーターを示しています。

パラメーター

タイプ

必須

説明

Accept

String

いいえ

応答形式。このパラメーターを指定しない場合、応答は XML 形式になります。ROA 形式の API オペレーションを呼び出すには、値を application/json に設定します。

application/json

Content-MD5

String

いいえ

リクエストボディの Base64 エンコードされた 128 ビット MD5 ハッシュ値。

LP54yxk8n7KqF1PPgbJizw==

Date

String

はい

リクエストのタイムスタンプ。タイムスタンプは 15 分間有効です。タイムスタンプが生成されてから 15 分以内にリクエストを開始する必要があります。HTTP 1.1 に準拠して、GMT で時間を指定します。例:Tue 9 Apr 2019 07:35:29 GMT。

Wed, 16 Apr 2025 03:44:46 GMT

Host

String

はい

Alibaba Cloud サービス API のエンドポイント。詳細については、このトピックのHTTP リクエスト構造セクションをご参照ください。

bailian.cn-beijing.aliyuncs.com

x-acs-signature-method

String

リクエストが匿名でない場合は必須

署名文字列の暗号化方式。値を HMAC-SHA1 に設定します。

HMAC-SHA1

x-acs-signature-nonce

String

はい

ネットワークリプレイ攻撃を防ぐために使用される一意の乱数。リプレイ攻撃を防ぐために、リクエストごとに異なる番号を使用することをお勧めします。

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

x-acs-signature-version

String

いいえ

署名バージョン。値を 1.0 に設定します。

1.0

x-acs-version

String

はい

API のバージョン。OpenAPI Explorer または API メタデータ で API バージョンを取得できます。

2023-12-29

Authorization

String

リクエストが匿名でない場合は必須

リクエストの有効性を検証するために使用される認証情報。AccessKeyId:Signature 形式で値を指定します。 AccessKeyId パラメーターは、Alibaba Cloud から提供された AccessKey ID を指定します。AccessKey ID は、Resource Access Management (RAM) コンソールで確認できます。AccessKey ペアの作成方法については、AccessKey ペアを取得するをご参照ください。

Signature パラメーターは、リクエストの署名文字列を指定します。詳細については、このトピックの署名方式セクションをご参照ください。

acs YourAccessKeyId:D9uFJAJgLL+dryjBfQK+YeqGtoY=

署名方式

セキュリティを確保するために、すべての HTTP および HTTPS API リクエストに署名する必要があります。リクエストが Alibaba Cloud API ゲートウェイに到達すると、ゲートウェイはリクエストパラメーターとリクエストヘッダーに基づいて署名を再計算し、計算された署名とリクエスト内の署名を比較して API 呼び出し元の ID を検証します。このメカニズムにより、データの整合性とセキュリティが確保されます。このセクションでは、署名の計算方法について説明します。

説明

すべてのリクエストとレスポンスは UTF-8 でエンコードされます。

ステップ 1:正規化ヘッダーを構築する

正規化ヘッダーは非標準 HTTP ヘッダーです。非標準 HTTP ヘッダーとは、リクエストで名前のプレフィックスが x-acs- であるヘッダーです。正規化ヘッダーを構築するには、次の手順を実行します。

  1. リクエストヘッダー 内のプレフィックスが x-acs- であるリクエストヘッダー名を小文字に変換し、辞書順に昇順でソートします。

  2. リクエストヘッダーの名前と値から先頭と末尾のスペースを削除します。

  3. 最後のヘッダーを含め、各ヘッダーの末尾に \n デリミターを追加し、すべてのヘッダーを連結します。

例:

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

ステップ 2:正規化リソースを構築する

正規化リソースは、アクセスするリソースの正規表現です。正規化リソースを構築するには、次の手順を実行します。

  1. リクエストのクエリ文字列内のパラメーターを名前でアルファベット順にソートし、アンパサンド(&)を使用してパラメーターを連結し、ソートされたクエリ文字列を生成します。

  2. 疑問符(?)を使用して、リクエストされたリソースパスとソートされたクエリ文字列を連結し、正規化リソースを取得します。リソースパスは、エンドポイントとクエリ文字列の間の部分です。パスには、エンドポイントの後のスラッシュ(/)が含まれますが、クエリ文字列の前の疑問符(?)は含まれません。リクエストにクエリ文字列が含まれていない場合は、リソースパスを正規化リソースとして使用します。

例:

/llm-p2e4XXXXXXXXsvtn/datacenter/category

ステップ 3:署名対象文字列を構築する

次の擬似コードに基づいて、署名対象文字列を作成します。

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

パラメーター

説明

HTTPMethod

POST や GET など、大文字の HTTP メソッド。

Accept

Accept ヘッダーの値。このヘッダーが存在しない場合は、空の文字列を設定します。

ContentMD5

Content-MD5 ヘッダーの値。このヘッダーが存在しない場合は、空の文字列を設定します。

ContentType

Content-Type ヘッダーの値。このヘッダーが存在しない場合は、空の文字列を設定します。

説明

リクエストボディの MIME(Multipurpose Internet Mail Extensions)タイプ。

Date

Date ヘッダーの値。

CanonicalizedHeaders

このトピックのステップ 1:正規化ヘッダーを構築するから取得した正規化ヘッダー。

CanonicalizedResource

このトピックのステップ 2:正規化リソースを構築するから取得した正規化リソース。

例:

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

ステップ 4:署名文字列を計算する

HMAC-SHA1 アルゴリズムを使用して、署名対象文字列の HMAC(Hash-based Message Authentication Code)値を計算し、Base64 エンコードルールに基づいて HMAC 値を署名文字列にエンコードします。HMAC の詳細については、RFC 2104 をご参照ください。

String signature = Base64(HMAC_SHA1(SigningKey, stringToSign))
重要

SigningKey パラメーターを AccessKey シークレットに設定します。詳細については、AccessKey ペアを作成するをご参照ください。

例:

s8ZMF/eAIvvPJwehLLha0bVNFJ0=

ステップ 5:Authorization ヘッダーを指定する

署名文字列を取得したら、次のコードに基づいて Authorization ヘッダーの値を構築します。

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

例:

acs YourAccessKeyId:s8ZMF/eAIvvPJwehLLha0bVNFJ0=

ステップ 6:リクエストに署名を追加し、リクエストを送信する

POST https://bailian.cn-beijing.aliyuncs.com/{WorkspaceId}/datacenter/category HTTP/1.1
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

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

署名例

Java

説明

この例では、JDK 1.8 ランタイム環境を使用しています。ビジネス要件に基づいてパラメーターを調整してください。

Java で署名メソッドを使用するには、次の Maven 依存関係を 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) {
        // 例 1: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);

        /*// 例 2: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");*/

        /*// 例 3: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);*/

        // Authorization ヘッダーの値を構築します。
        generateSignature(signatureRequest);
        // HTTPClient を使用してリクエストを送信します。
        callApi(signatureRequest);
    }

    private static void generateSignature(SignatureRequest signatureRequest) {
        try {
            // ステップ 1:正規化されたヘッダーを構築する
            String canonicalHeaders = buildCanonicalHeaders(signatureRequest);
            // ステップ 2:正規化されたリソースを構築する
            String canonicalQueryString = buildQueryString(signatureRequest);
            // ステップ 3:署名対象の文字列を構築する
            String stringToSign = buildStringToSign(signatureRequest, canonicalQueryString, canonicalHeaders);
            // ステップ 4:署名文字列を計算する
            String signature = signString(stringToSign);
            // ステップ 5: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 {
            // HttpClient を使用してリクエストを送信します。
            String url = "https://" + signatureRequest.host + signatureRequest.canonicalUri;
            URIBuilder uriBuilder = new URIBuilder(url);
            // リクエスト パラメーターを指定します。
            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("サポートされていない HTTP メソッド: " + signatureRequest.httpMethod);
                    throw new IllegalArgumentException("サポートされていない HTTP メソッド");
            }
            // HTTP リクエスト ヘッダーを指定します。
            signatureRequest.headers.forEach(httpRequest::addHeader);
            // リクエストを送信します。
            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) {
                // エラーを処理します。
                System.out.println("リクエストの送信に失敗しました");
                throw new IllegalArgumentException(e.toString());
            }
        } catch (URISyntaxException e) {
            // エラーを処理します。
            System.out.println("無効な URI 構文");
            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());
        }
    }
}

参照資料

RPC スタイル API と ROA スタイル API の違いの詳細については、「API スタイル」をご参照ください。