通过OpenAPI获取Token

更新时间:
复制 MD 格式

当所用编程语言没有对应的SDK,或需要自行控制依赖组件时,可以直接调用OpenAPI获取Token。本文介绍请求参数、签名机制和响应格式,并提供Java和Python示例。

准备访问凭证

调用接口需要AccessKey ID和AccessKey Secret。将访问凭证配置到运行环境的 ALIYUN_AK_ID 和 ALIYUN_AK_SECRET 环境变量中,示例代码从环境变量读取凭证。

重要

AccessKey Secret和Token均为敏感信息,不要写入公开代码或日志。以下示例会输出包含Token的响应,集成到应用时应妥善保管。

代码示例

以下示例通过HTTPS GET请求获取Token。响应中的 Token.Id 为Token值,Token.ExpireTime 为过期时间戳,单位为秒。

Java

使用JDK 8或更高版本,并添加以下Maven依赖。

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.9.1</version>
</dependency>
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
import java.util.Map;
import java.util.StringJoiner;
import java.util.TreeMap;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class CreateToken {
    private static String encode(String value) throws Exception {
        return URLEncoder.encode(value, "UTF-8")
                .replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
    }

    public static void main(String[] args) throws Exception {
        String accessKeyId = System.getenv("ALIYUN_AK_ID");
        String accessKeySecret = System.getenv("ALIYUN_AK_SECRET");
        if (accessKeyId == null || accessKeyId.isEmpty()
                || accessKeySecret == null || accessKeySecret.isEmpty()) {
            throw new IllegalArgumentException("Set ALIYUN_AK_ID and ALIYUN_AK_SECRET.");
        }

        String endpoint = "https://nls-meta.cn-shanghai.aliyuncs.com/";
        Map<String, String> parameters = new TreeMap<>();
        parameters.put("AccessKeyId", accessKeyId);
        parameters.put("Action", "CreateToken");
        parameters.put("Version", "2019-02-28");
        parameters.put("RegionId", "cn-shanghai");
        parameters.put("Format", "JSON");
        parameters.put("SignatureMethod", "HMAC-SHA1");
        parameters.put("SignatureVersion", "1.0");
        parameters.put("SignatureNonce", UUID.randomUUID().toString());
        parameters.put("Timestamp", Instant.now().truncatedTo(ChronoUnit.SECONDS).toString());

        StringJoiner query = new StringJoiner("&");
        for (Map.Entry<String, String> entry : parameters.entrySet()) {
            query.add(encode(entry.getKey()) + "=" + encode(entry.getValue()));
        }
        String canonicalQuery = query.toString();
        String stringToSign = "GET&" + encode("/") + "&" + encode(canonicalQuery);
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(new SecretKeySpec((accessKeySecret + "&").getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
        String signature = Base64.getEncoder().encodeToString(
                mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8)));

        Request request = new Request.Builder()
                .url(endpoint + "?" + canonicalQuery + "&Signature=" + encode(signature))
                .get().build();
        OkHttpClient client = new OkHttpClient();
        try (Response response = client.newCall(request).execute()) {
            String body = response.body() == null ? "" : response.body().string();
            if (!response.isSuccessful()) {
                throw new IllegalStateException("HTTP " + response.code() + ": " + body);
            }
            System.out.println(body);
        }
    }
}

Python

使用Python 3.4或更高版本,安装Requests库。示例使用Python 3.14.7。

pip install requests
import base64
import hashlib
import hmac
import os
import uuid
from datetime import datetime, timezone
from urllib.parse import quote

import requests


def encode(value):
    return quote(value, safe="-_.~")


def create_token():
    access_key_id = os.environ["ALIYUN_AK_ID"]
    access_key_secret = os.environ["ALIYUN_AK_SECRET"]
    endpoint = "https://nls-meta.cn-shanghai.aliyuncs.com/"
    parameters = {
        "AccessKeyId": access_key_id,
        "Action": "CreateToken",
        "Version": "2019-02-28",
        "RegionId": "cn-shanghai",
        "Format": "JSON",
        "SignatureMethod": "HMAC-SHA1",
        "SignatureVersion": "1.0",
        "SignatureNonce": str(uuid.uuid4()),
        "Timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
    }
    canonical_query = "&".join(
        encode(key) + "=" + encode(value)
        for key, value in sorted(parameters.items())
    )
    string_to_sign = "GET&" + encode("/") + "&" + encode(canonical_query)
    signature = base64.b64encode(hmac.new(
        (access_key_secret + "&").encode("utf-8"),
        string_to_sign.encode("utf-8"), hashlib.sha1
    ).digest()).decode("ascii")
    response = requests.get(
        endpoint + "?" + canonical_query + "&Signature=" + encode(signature),
        timeout=20,
    )
    if response.status_code != 200:
        raise RuntimeError("HTTP {}: {}".format(response.status_code, response.text))
    return response.text


if __name__ == "__main__":
    print(create_token())

请求说明

接口支持GET和POST方法,使用阿里云POP协议进行签名。HTTP和HTTPS请求的参数相同,建议使用HTTPS。

服务地址

Endpoint:https://nls-meta.cn-shanghai.aliyuncs.com/,RegionId:cn-shanghai,API版本:2019-02-28。

请求参数

名称

类型

必选

说明

AccessKeyId

String

是

AccessKey ID。

Action

String

是

接口名称,固定为 CreateToken。

Version

String

是

API版本,固定为 2019-02-28。

Format

String

否

响应格式。设为 JSON 可返回JSON;不设置时返回XML。本文示例设置为 JSON。

RegionId

String

是

地域ID,固定为 cn-shanghai。

Timestamp

String

是

请求时间戳,使用UTC时间,格式为 yyyy-MM-dd'T'HH:mm:ss'Z',例如 2019-04-03T06:15:03Z。实际请求使用当前时间。

SignatureMethod

String

是

签名算法,固定为 HMAC-SHA1。

SignatureVersion

String

是

签名算法版本,固定为 1.0。

SignatureNonce

String

是

唯一随机字符串,用于防止重放攻击,每次请求不得重复。可使用UUID,例如 8d1e6a7a-f44e-40d5-aedb-fe4a1c80f434。

Signature

String

是

根据除Signature外的全部请求参数计算得到的签名,计算方法见下文签名机制。

请求头与参数传递

GET请求将参数放在URL查询字符串中;POST请求将参数放在请求体中。

请求头

必选

说明

Host

否

服务域名,通常由HTTP客户端根据Endpoint自动设置。

Accept

否

可设置为 application/json,表示客户端接受JSON响应。未设置时通常为 */*。响应格式通过Format参数指定。

Content-Type

POST必选

POST请求设置为 application/x-www-form-urlencoded。

请求格式如下,<encoded-parameters> 表示URL编码后的参数字符串,包含Signature。POST请求计算签名时使用 POST,不能直接复用GET签名。

GET

GET /?<encoded-parameters> HTTP/1.1
Host: <endpoint-host>
Accept: application/json

POST

POST / HTTP/1.1
Host: <endpoint-host>
Accept: application/json
Content-Type: application/x-www-form-urlencoded

<encoded-parameters>

签名机制

每个请求都必须携带签名。客户端使用AccessKey Secret计算HMAC-SHA1消息认证码;服务端根据请求中的AccessKey ID查找对应密钥,并校验签名,以验证身份和请求完整性。

构造规范化请求字符串

  1. 排除Signature参数,按参数名进行区分大小写的字典排序。

  2. 对参数名和参数值分别进行UTF-8 URL编码:

    • A-Z、a-z、0-9、-、_、.、~ 不编码。

    • 其他字符按其UTF-8字节编码为 %XY,例如双引号编码为 %22。

    • 空格编码为 %20,不能编码为加号 +。

  3. 使用 = 连接编码后的参数名和值,再用 & 连接各参数对。第一个参数前不加 &。

percentEncode(name1)=percentEncode(value1)&percentEncode(name2)=percentEncode(value2)

Java的 URLEncoder 按表单规则编码,需将编码结果中的 + 替换为 %20、* 替换为 %2A、%7E 替换为 ~。

构造待签名字符串

将大写HTTP方法、编码后的请求路径 / 和再次编码的规范化请求字符串用 & 连接。

StringToSign = HTTPMethod + "&" + percentEncode("/") + "&" + percentEncode(CanonicalizedQueryString)

计算并添加签名

  1. 在AccessKey Secret末尾追加 &,作为HMAC-SHA1密钥。

  2. 使用UTF-8编码的密钥和待签名字符串计算HMAC-SHA1,再对结果进行Base64编码。

  3. 对Base64结果进行URL编码,以 Signature=<encoded-signature> 的形式加入请求参数。

Signature = Base64(HMAC-SHA1(AccessKeySecret + "&", StringToSign))

签名校验示例

以下固定参数用于离线核对签名实现。AccessKey为虚拟值,Timestamp已过期,不能用于实际获取Token。

AccessKeyId: my_access_key_id
AccessKeySecret: my_access_key_secret
Action: CreateToken
Format: JSON
SignatureMethod: HMAC-SHA1
SignatureVersion: 1.0
SignatureNonce: b924c8c3-6d03-4c5d-ad36-d984d3116788
Timestamp: 2019-04-18T08:32:31Z
HTTPMethod: GET

规范化请求字符串:

AccessKeyId=my_access_key_id&Action=CreateToken&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=b924c8c3-6d03-4c5d-ad36-d984d3116788&SignatureVersion=1.0&Timestamp=2019-04-18T08%3A32%3A31Z&Version=2019-02-28

待签名字符串:

GET&%2F&AccessKeyId%3Dmy_access_key_id%26Action%3DCreateToken%26Format%3DJSON%26RegionId%3Dcn-shanghai%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3Db924c8c3-6d03-4c5d-ad36-d984d3116788%26SignatureVersion%3D1.0%26Timestamp%3D2019-04-18T08%253A32%253A31Z%26Version%3D2019-02-28

Base64签名:

hHq4yNsPitlfDJ2L0nQPdugdEzM=

URL编码后的签名参数:

Signature=hHq4yNsPitlfDJ2L0nQPdugdEzM%3D

响应说明

以下说明适用于 Format=JSON。GET和POST方法的响应格式相同。

成功响应

HTTP状态码为200,主要字段如下。

字段

类型

说明

Token

Object

包含Token值和过期时间。

Token.Id

String

Token值。

Token.ExpireTime

Long

Token的过期时间,以Unix时间戳表示,单位为秒;不是有效时长。例如1553825814对应UTC时间2019-03-29 02:16:54,Token在该时间之前有效。

响应中的Token字段示例如下。

{
  "Token": {
    "Id": "<token>",
    "ExpireTime": 1553825814
  }
}

失败响应

HTTP状态码为非200,根据Code和Message检查请求参数。

字段

类型

说明

RequestId

String

请求ID,可用于排查问题。

Code

String

错误码。

Message

String

错误说明。

例如,AccessKey ID不存在时,返回HTTP 404,响应中包含以下字段。

{
  "RequestId": "<request-id>",
  "Code": "InvalidAccessKeyId.NotFound",
  "Message": "Specified access key is not found."
}

相关文档