全部产品
云市场

获取Token协议说明

更新时间:2019-10-24 20:46:45

访问令牌(Access Token)是调用智能语音服务的凭证。我们提供了基于阿里云公共SDK调用云端服务获取Access Token的方式。如果您希望自己实现获取Access Token的客户端程序,请阅读下面的获取Access Token的接口协议说明。

请求报文实例

客户端向服务端发送获取Token的请求,服务端返回创建Token结果的响应。客户端发送的请求支持使用HTTP或者HTTPS协议,请求方法支持GET或者POST方法。服务端提供了基于阿里云POP协议的接口,因此客户端需要实现阿里云POP的签名机制。

由于HTTPS协议的请求参数设置与HTTP协议相同,下面将以HTTP协议请求为例,介绍如何发送获取Token请求。

URL

协议 URL 方法
HTTP/1.1 http://nls-meta.cn-shanghai.aliyuncs.com/ GET 或 POST

请求参数

说明:

  • 使用GET方法,需要将请求参数设置到请求行中:/?请求参数字符串
  • 使用POST方法,需要将请求参数设置到请求的Body中。
名称 类型 是否必需 说明
AccessKeyId String 阿里云颁发给您的访问服务所用的密钥ID,请填入您的阿里云账号的AccessKey ID
Action String POP API名称:CreateToken
Version String POP API版本:2019-02-28
Format String 响应返回的类型:JSON
RegionId String 服务所在的地域ID:cn-shanghai
Timestamp String 请求的时间戳。日期格式按照ISO 8601标准表示,并需要使用UTC时间,时区为:+0(请勿使用本地时间,如北京时间)。格式为YYYY-MM-DDThh:mm:ssZ。例如2019-04-03T06:15:03Z 为UTC时间2019年4月3日6点15分03秒。
SignatureMethod String 签名算法:HMAC-SHA1
SignatureVersion String 签名算法版本:1.0
SignatureNonce String 唯一随机数uuid,用于请求的防重放攻击,每次请求唯一,不能重复使用。格式为xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx(8-4-4-4-12),例如8d1e6a7a-f44e-40d5-aedb-fe4a1c80f434
Signature String 由所有请求参数计算出的签名结果,生成方法请参考下文签名机制。

HTTP请求头部

HTTP 请求头部由“关键字/值”对组成,每行一对,关键字和值用英文冒号“:”分隔,设置内容为如下表格:

名称 类型 是否必需 描述
Host String HTTP请求的服务器域名:nls-meta.cn-shanghai.aliyuncs.com,一般会根据请求链接自动解析
Accept String 指定客户端能够接收的内容类型:application/json,不设置默认为 */*
Content-type String POST方法必须设置 指定POST方法请求的Body数据格式:application/x-www-form-urlencoded

报文示例

HTTP GET请求报文:

  1. GET /?Signature=O0s6pfeOxtFM6YKSZKQdSyPR9Vs%3D&AccessKeyId=LTA******F3s&Action=CreateToken&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=a1f01895-6ff1-43c1-ba15-6c109fa00106&SignatureVersion=1.0&Timestamp=2019-03-27T09%3A51%3A25Z&Version=2019-02-28 HTTP/1.1
  2. Host: nls-meta.cn-shanghai.aliyuncs.com
  3. User-Agent: curl/7.49.1
  4. Accept: */*

HTTP POST请求报文:

  1. POST / HTTP/1.1
  2. Host: nls-meta.cn-shanghai.aliyuncs.com
  3. User-Agent: curl/7.49.1
  4. Accept: */*
  5. Content-type: application/x-www-form-urlencoded
  6. Content-Length: 276
  7. SignatureVersion=1.0&Action=CreateToken&Format=JSON&SignatureNonce=8d1e6a7a-f44e-40d5-aedb-fe4a1c80f434&Version=2019-02-28&AccessKeyId=LTA******F3s&Signature=oT8A8RgvFE1tMD%2B3hDbGuoMQSi8%3D&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&Timestamp=2019-03-25T09%3A07%3A52Z

响应结果

发送获取Token的HTTP请求之后,会收到服务端的响应,结果以JSON字符串的形式保存在该响应中。GET方法和POST方法的响应结果相同。

  • 成功响应HTTP状态码为200,响应字段说明:
参数 类型 说明
Token token对象 包含了具体的token值和有效期时间戳
Id String 本次请求分配的token值
ExpireTime Long token的有效期时间戳(单位:秒,例如1553825814换算为北京时间为:2019/3/29 10:16:54,即token在该时间之前有效。)
  1. HTTP/1.1 200 OK
  2. Date: Mon, 25 Mar 2019 09:29:24 GMT
  3. Content-Type: application/json; charset=UTF-8
  4. Content-Length: 216
  5. Connection: keep-alive
  6. Access-Control-Allow-Origin: *
  7. Access-Control-Allow-Methods: POST, GET, OPTIONS
  8. Access-Control-Allow-Headers: X-Requested-With, X-Sequence, _aop_secret, _aop_signature
  9. Access-Control-Max-Age: 172800
  10. Server: Jetty(7.2.2.v20101205)
  11. {"NlsRequestId":"dd05a301b40441c99a2671905325fb1f","RequestId":"E11F2DC2-0163-4D97-A704-0BD28045608A","ErrMsg":"","Token":{"ExpireTime":1553592564,"Id":"889******166","UserId":"150**********151"}}

响应Body JSON字符串格式解析:

  1. {
  2. "NlsRequestId": "dd05a301b40441c99a2671905325fb1f",
  3. "RequestId": "E11F2DC2-0163-4D97-A704-0BD28045608A",
  4. "ErrMsg": "",
  5. "Token": {
  6. "ExpireTime": 1553592564,
  7. "Id": "889******166",
  8. "UserId": "150**********151"
  9. }
  10. }
  • 失败响应HTTP状态码为非200,响应字段说明:
参数 类型 说明
RequestId String 请求ID
Message String 失败响应的错误信息
Code String 失败响应的错误码

说明: 请根据错误码和错误信息提示检查请求参数是否设置正确,如不能排查,请将响应信息提交到工单。

以重传入阿里云账号的AccessKey Id错误为例:

  1. HTTP/1.1 404 Not Found
  2. Date: Thu, 28 Mar 2019 07:23:01 GMT
  3. Content-Type: application/json; charset=UTF-8
  4. Content-Length: 290
  5. Connection: keep-alive
  6. Access-Control-Allow-Origin: *
  7. Access-Control-Allow-Methods: POST, GET, OPTIONS
  8. Access-Control-Allow-Headers: X-Requested-With, X-Sequence, _aop_secret, _aop_signature
  9. Access-Control-Max-Age: 172800
  10. Server: Jetty(7.2.2.v20101205)
  11. {"Recommend":"https://error-center.aliyun.com/status/search?Keyword=InvalidAccessKeyId.NotFound&source=PopGw","Message":"Specified access key is not found.","RequestId":"A51587CB-5193-4DB8-9AED-CD4365C2D1E1","HostId":"nls-meta.cn-shanghai.aliyuncs.com","Code":"InvalidAccessKeyId.NotFound"}

响应Body JSON字符串格式解析:

  1. {
  2. "Recommend": "https://error-center.aliyun.com/status/search?Keyword=InvalidAccessKeyId.NotFound&source=PopGw",
  3. "Message": "Specified access key is not found.",
  4. "RequestId": "A51587CB-5193-4DB8-9AED-CD4365C2D1E1",
  5. "HostId": "nls-meta.cn-shanghai.aliyuncs.com",
  6. "Code": "InvalidAccessKeyId.NotFound"
  7. }

签名机制

服务端POP API对每个接口访问请求的发送者都要进行身份验证,所以无论使用HTTP协议还是HTTPS协议提交的请求,都需要在请求中包含签名信息。通过签名机制,服务端可以确认哪位客户在做API请求,并能确认客户请求在网络传输过程中有无被篡改。

安全验证流程

计算签名时,需要您的阿里云账号的AccessKeyId 和 AccessKeySecret,使用HMAC-SHA1算法进行对称加密。其工作流程如下:

  1. 请求端根据API请求内容(包括HTTP 请求参数和Body)生成签名字符串。
  2. 请求端使用阿里云账号的AccessKeyId 和 AccessKeySecret对第一步生成的签名字符串进行签名,获得该API请求的数字签名。
  3. 请求端把API请求内容和数字签名一同发送给服务端。
  4. 服务端在接收到请求后会重复如上的第1、2步工作(服务端会在后台获取该请求使用的用户秘钥)并在服务端计算出该请求期望的数字签名。
  5. 服务端用期望的数字签名和请求端发送过来的数字签名做对比,如果完全一致则认为该请求通过验证。否则直接拒绝该请求。

生成请求的签名字符串

1. 构造规范化的请求字符串

将HTTP的请求参数(不包括Signature)构造成规范化的请求字符串。规范化步骤:

  1. 参数排序。按参数名的字典顺序,对请求参数进行排序,严格按照大小写敏感排序
  2. 编码参数,对排序后的请求参数进行规范化设置。 请求参数的名称和值都要使用UTF-8字符集进行URL编码,URL编码规则如下:
    • 对于字符 A-Z、a-z、0-9以及字符-_.~不编码;
    • 对于其他字符编码成“%XY”的格式,其中XY是字符对应ASCII码的16进制表示。比如英文的双引号(”)对应的编码就是%22;
    • 对于扩展的UTF-8字符,编码成“%XY%ZA…”的格式;
    • 需要说明的是英文空格要被编码是%20,而不是加号+;注:一般支持URL编码的库(比如Java中的java.net.URLEncoder)都是按照“application/x-www-form-urlencoded”的MIME类型的规则进行编码的。实现时可以直接使用此类方式进行编码,然后把编码后的字符串中:加号+替换为%20,星号*替换为%2A%7E替换为波浪号~,即可得到上述规则描述的编码字符串。
  3. 使用等号=连接URL编码后的参数名和参数值:percentEncode(参数Key) + “=” + percentEncode(参数值)
  4. 使用与号&连接第4步URL编码后的请求参数对,例如Action=CreateToken&Format=JSON
  5. 返回规范化的请求字符串(注意:字符串中第一个参数名前面不需要 & 符号)。

构造规范化的请求字符串代码示例:

  1. String percentEncode(String value) throws UnsupportedEncodingException {
  2. return value != null ? URLEncoder.encode(value, URL_ENCODING)
  3. .replace("+", "%20")
  4. .replace("*", "%2A")
  5. .replace("%7E", "~") : null;
  6. }
  7. // 对参数Key排序
  8. String[] sortedKeys = queryParamsMap.keySet().toArray(new String[] {});
  9. Arrays.sort(sortedKeys);
  10. // 对排序的参数进行编码、拼接
  11. for (String key : sortedKeys) {
  12. canonicalizedQueryString.append("&")
  13. .append(percentEncode(key)).append("=")
  14. .append(percentEncode(queryParamsMap.get(key)));
  15. }
  16. queryString = canonicalizedQueryString.toString().substring(1);

完整代码实现请阅读完整示例的canonicalizedQuery函数。

构造规范化的请求字符串:

  1. AccessKeyId=LTA******3s2&Action=CreateToken&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=f20b1beb-e5dc-4245-9e96-aa582e905c1a&SignatureVersion=1.0&Timestamp=2019-04-03T03%3A40%3A13Z&Version=2019-02-28

2. 构造待签名字符串

将HTTP请求的方法(GET)、URL编码的URL路径(/)、URL编码的第1步获取的规范化的请求字符串使用与符号&连接成待签名字符串:

HTTPMethod + "&" + percentEncode("/") + "&" + percentEncode(queryString)

构造签名字符串代码示例:

  1. StringBuilder strBuilderSign = new StringBuilder();
  2. strBuilderSign.append(HTTPMethod);
  3. strBuilderSign.append("&");
  4. strBuilderSign.append(percentEncode(urlPath));
  5. strBuilderSign.append("&");
  6. strBuilderSign.append(percentEncode(queryString));
  7. stringToSign = strBuilderSign.toString();

完整代码实现请阅读完整示例的createStringToSign函数。

构造的签名字符串:strToSign

3. 计算签名

  • 签名采用HMAC-SHA1算法 + Base64,编码采用UTF-8。
  • 根据您的AccessKeySecret,将第2步构造的待签名字符串使用HMAC-SHA1算法计算出对应的数字签名。其中,计算签名时使用的AccessKeySecret必须在其后面增加一个与字符&
  • 签名也要做URL编码。

计算签名的代码示例:

  1. signature = Base64( HMAC-SHA1(stringToSign, accessKeySecret + "&") );
  2. // 进行URL编码
  3. signature = percentEncode(signature)

完整代码实现请阅读完整示例的sign函数。

计算得到的签名:

  1. # 签名串
  2. AKIktdPUMCV12fTh667BLXeuCtg=
  3. # URL编码后的结果
  4. AKIktdPUMCV12fTh667BLXeuCtg%3D

计算签名后,将签名的键值对用符号=连接,并使用符号&添加到第1步获取的请求字符串中,作为HTTP GET请求参数,发送到服务端,获取token。

  1. String queryStringWithSign = "Signature=" + signature + "&" + queryString;

快速测试

您可以使用以下参数,计算签名,比较计算结果是否一致。

说明:AccessKeyId和AccessKeySecret没有提供真实数据,Timestamp是过期的时间,使用这些参数计算的签名,获取Token时会失败,仅用于计算签名测试对比。

  • AccessKeyId: my_access_key_id
  • AccessKeySecret: my_access_key_secret
  • Timestamp: 2019-04-18T08:32:31Z
  • SignatureNonce: b924c8c3-6d03-4c5d-ad36-d984d3116788

请求参数如下

  1. AccessKeyId:my_access_key_id
  2. Action:CreateToken
  3. Version:2019-02-28
  4. Timestamp:2019-04-18T08:32:31Z
  5. Format:JSON
  6. RegionId:cn-shanghai
  7. SignatureMethod:HMAC-SHA1
  8. SignatureVersion:1.0
  9. SignatureNonce:b924c8c3-6d03-4c5d-ad36-d984d3116788

1.规范化后的请求字符串:

  1. 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

2.构造的待签名字符串:

格式如下:

HTTPMethod + "&" + percentEncode("/") + "&" + percentEncode(queryString)

stringToSign

3.计算得到的签名:

  1. hHq4yNsPitlfDJ2L0nQPdugdEzM=
  2. # URL 编码后的结果
  3. hHq4yNsPitlfDJ2L0nQPdugdEzM%3D

4.带有签名的请求字符串:

  1. Signature=hHq4yNsPitlfDJ2L0nQPdugdEzM%3D&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

5.组合成的HTTP请求链接:

  1. http://nls-meta.cn-shanghai.aliyuncs.com/?Signature=hHq4yNsPitlfDJ2L0nQPdugdEzM%3D&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

6.替换第5步获取的HTTP请求链接,使用浏览器或者curl,获取Token:

  1. curl "http://nls-meta.cn-shanghai.aliyuncs.com/?Signature=${您的签名}&AccessKeyId=${您的AccessKey Id}&Action=CreateToken&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=${您的请求UUID}&SignatureVersion=1.0&Timestamp=${您的请求时间戳}&Version=2019-02-28"

完整示例

说明: 文档提供了Java、Python语言示例代码,您可根据协议和示例实现其他语言的客户端程序。

Java Demo

依赖:

  1. <!-- http://mvnrepository.com/artifact/com.alibaba/fastjson -->
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>fastjson</artifactId>
  5. <version>1.2.49</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>com.squareup.okhttp3</groupId>
  9. <artifactId>okhttp</artifactId>
  10. <version>3.9.1</version>
  11. </dependency>

Demo程序:

  1. import com.alibaba.fastjson.JSON;
  2. import com.alibaba.fastjson.JSONObject;
  3. import okhttp3.OkHttpClient;
  4. import okhttp3.Request;
  5. import okhttp3.Response;
  6. import javax.crypto.Mac;
  7. import javax.crypto.spec.SecretKeySpec;
  8. import javax.xml.bind.DatatypeConverter;
  9. import java.io.UnsupportedEncodingException;
  10. import java.net.URLEncoder;
  11. import java.security.InvalidKeyException;
  12. import java.security.NoSuchAlgorithmException;
  13. import java.text.SimpleDateFormat;
  14. import java.util.Arrays;
  15. import java.util.Date;
  16. import java.util.HashMap;
  17. import java.util.Map;
  18. import java.util.SimpleTimeZone;
  19. import java.util.UUID;
  20. public class CreateToken {
  21. private final static String TIME_ZONE = "GMT";
  22. private final static String FORMAT_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
  23. private final static String URL_ENCODING = "UTF-8";
  24. private static final String ALGORITHM_NAME = "HmacSHA1";
  25. private static final String ENCODING = "UTF-8";
  26. private static String token = null;
  27. private static long expireTime = 0;
  28. /**
  29. * 获取时间戳
  30. * 必须符合ISO8601规范,并需要使用UTC时间,时区为+0
  31. */
  32. public static String getISO8601Time(Date date) {
  33. Date nowDate = date;
  34. if (null == date) {
  35. nowDate = new Date();
  36. }
  37. SimpleDateFormat df = new SimpleDateFormat(FORMAT_ISO8601);
  38. df.setTimeZone(new SimpleTimeZone(0, TIME_ZONE));
  39. return df.format(nowDate);
  40. }
  41. /**
  42. * 获取UUID
  43. */
  44. public static String getUniqueNonce() {
  45. UUID uuid = UUID.randomUUID();
  46. return uuid.toString();
  47. }
  48. /**
  49. * URL编码
  50. * 使用UTF-8字符集按照 RFC3986 规则编码请求参数和参数取值
  51. */
  52. public static String percentEncode(String value) throws UnsupportedEncodingException {
  53. return value != null ? URLEncoder.encode(value, URL_ENCODING).replace("+", "%20")
  54. .replace("*", "%2A").replace("%7E", "~") : null;
  55. }
  56. /***
  57. * 将参数排序后,进行规范化设置,组合成请求字符串
  58. * @param queryParamsMap 所有请求参数
  59. * @return 规范化的请求字符串
  60. */
  61. public static String canonicalizedQuery( Map<String, String> queryParamsMap) {
  62. String[] sortedKeys = queryParamsMap.keySet().toArray(new String[] {});
  63. Arrays.sort(sortedKeys);
  64. String queryString = null;
  65. try {
  66. StringBuilder canonicalizedQueryString = new StringBuilder();
  67. for (String key : sortedKeys) {
  68. canonicalizedQueryString.append("&")
  69. .append(percentEncode(key)).append("=")
  70. .append(percentEncode(queryParamsMap.get(key)));
  71. }
  72. queryString = canonicalizedQueryString.toString().substring(1);
  73. System.out.println("规范化后的请求参数串:" + queryString);
  74. } catch (UnsupportedEncodingException e) {
  75. System.out.println("UTF-8 encoding is not supported.");
  76. e.printStackTrace();
  77. }
  78. return queryString;
  79. }
  80. /***
  81. * 构造签名字符串
  82. * @param method HTTP请求的方法
  83. * @param urlPath HTTP请求的资源路径
  84. * @param queryString 规范化的请求字符串
  85. * @return 签名字符串
  86. */
  87. public static String createStringToSign(String method, String urlPath, String queryString) {
  88. String stringToSign = null;
  89. try {
  90. StringBuilder strBuilderSign = new StringBuilder();
  91. strBuilderSign.append(method);
  92. strBuilderSign.append("&");
  93. strBuilderSign.append(percentEncode(urlPath));
  94. strBuilderSign.append("&");
  95. strBuilderSign.append(percentEncode(queryString));
  96. stringToSign = strBuilderSign.toString();
  97. System.out.println("构造的签名字符串:" + stringToSign);
  98. } catch (UnsupportedEncodingException e) {
  99. System.out.println("UTF-8 encoding is not supported.");
  100. e.printStackTrace();
  101. }
  102. return stringToSign;
  103. }
  104. /***
  105. * 计算签名
  106. * @param stringToSign 签名字符串
  107. * @param accessKeySecret 阿里云AccessKey Secret加上与号&
  108. * @return 计算得到的签名
  109. */
  110. public static String sign(String stringToSign, String accessKeySecret) {
  111. try {
  112. Mac mac = Mac.getInstance(ALGORITHM_NAME);
  113. mac.init(new SecretKeySpec(
  114. accessKeySecret.getBytes(ENCODING),
  115. ALGORITHM_NAME
  116. ));
  117. byte[] signData = mac.doFinal(stringToSign.getBytes(ENCODING));
  118. String signBase64 = DatatypeConverter.printBase64Binary(signData);
  119. System.out.println("计算的得到的签名:" + signBase64);
  120. String signUrlEncode = percentEncode(signBase64);
  121. System.out.println("UrlEncode编码后的签名:" + signUrlEncode);
  122. return signUrlEncode;
  123. } catch (NoSuchAlgorithmException e) {
  124. throw new IllegalArgumentException(e.toString());
  125. } catch (UnsupportedEncodingException e) {
  126. throw new IllegalArgumentException(e.toString());
  127. } catch (InvalidKeyException e) {
  128. throw new IllegalArgumentException(e.toString());
  129. }
  130. }
  131. /***
  132. * 发送HTTP GET请求,获取token和有效期时间戳
  133. * @param queryString 请求参数
  134. */
  135. public static void processGETRequest(String queryString) {
  136. /**
  137. * 设置HTTP GET请求
  138. * 1. 使用HTTP协议
  139. * 2. Token服务域名:nls-meta.cn-shanghai.aliyuncs.com
  140. * 3. 请求路径:/
  141. * 4. 设置请求参数
  142. */
  143. String url = "http://nls-meta.cn-shanghai.aliyuncs.com";
  144. url = url + "/";
  145. url = url + "?" + queryString;
  146. System.out.println("HTTP请求链接:" + url);
  147. Request request = new Request.Builder()
  148. .url(url)
  149. .header("Accept", "application/json")
  150. .get()
  151. .build();
  152. try {
  153. OkHttpClient client = new OkHttpClient();
  154. Response response = client.newCall(request).execute();
  155. String result = response.body().string();
  156. if (response.isSuccessful()) {
  157. JSONObject rootObj = JSON.parseObject(result);
  158. JSONObject tokenObj = rootObj.getJSONObject("Token");
  159. if (tokenObj != null) {
  160. token = tokenObj.getString("Id");
  161. expireTime = tokenObj.getLongValue("ExpireTime");
  162. }
  163. else{
  164. System.err.println("提交获取Token请求失败: " + result);
  165. }
  166. }
  167. else {
  168. System.err.println("提交获取Token请求失败: " + result);
  169. }
  170. response.close();
  171. } catch (Exception e) {
  172. e.printStackTrace();
  173. }
  174. }
  175. public static void main(String args[]) {
  176. if (args.length < 2) {
  177. System.err.println("CreateTokenDemo need params: <AccessKey Id> <AccessKey Secret>");
  178. System.exit(-1);
  179. }
  180. String accessKeyId = args[0];
  181. String accessKeySecret = args[1];
  182. System.out.println(getISO8601Time(null));
  183. // 所有请求参数
  184. Map<String, String> queryParamsMap = new HashMap<String, String>();
  185. queryParamsMap.put("AccessKeyId", accessKeyId);
  186. queryParamsMap.put("Action", "CreateToken");
  187. queryParamsMap.put("Version", "2019-02-28");
  188. queryParamsMap.put("Timestamp", getISO8601Time(null));
  189. queryParamsMap.put("Format", "JSON");
  190. queryParamsMap.put("RegionId", "cn-shanghai");
  191. queryParamsMap.put("SignatureMethod", "HMAC-SHA1");
  192. queryParamsMap.put("SignatureVersion", "1.0");
  193. queryParamsMap.put("SignatureNonce", getUniqueNonce());
  194. /**
  195. * 1.构造规范化的请求字符串
  196. */
  197. String queryString = canonicalizedQuery(queryParamsMap);
  198. if (null == queryString) {
  199. System.out.println("构造规范化的请求字符串失败!");
  200. return;
  201. }
  202. /**
  203. * 2.构造签名字符串
  204. */
  205. String method = "GET"; // 发送请求的 HTTP 方法,GET
  206. String urlPath = "/"; // 请求路径
  207. String stringToSign = createStringToSign(method, urlPath, queryString);
  208. if (null == stringToSign) {
  209. System.out.println("构造签名字符串失败");
  210. return;
  211. }
  212. /**
  213. * 3.计算签名
  214. */
  215. String signature = sign(stringToSign, accessKeySecret + "&");
  216. if (null == signature) {
  217. System.out.println("计算签名失败!");
  218. return;
  219. }
  220. /**
  221. * 4.将签名加入到第1步获取的请求字符串
  222. */
  223. String queryStringWithSign = "Signature=" + signature + "&" + queryString;
  224. System.out.println("带有签名的请求字符串:" + queryStringWithSign);
  225. /**
  226. * 5.发送HTTP GET请求,获取token
  227. */
  228. processGETRequest(queryStringWithSign);
  229. if (token != null) {
  230. System.out.println("获取的Token:" + token + ", 有效期时间戳(秒):" + expireTime);
  231. // 将10位数的时间戳转换为北京时间
  232. String expireDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(expireTime * 1000));
  233. System.out.println("Token有效期的北京时间:" + expireDate);
  234. }
  235. }
  236. }

Python Demo

说明:

  • Python版本要求:Python3.4及以上。
  • 请安装Python HTTP库Requests
    1. pip install requests

Demo程序:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import base64
  4. import hashlib
  5. import hmac
  6. import requests
  7. import time
  8. import uuid
  9. from urllib import parse
  10. class AccessToken:
  11. @staticmethod
  12. def _encode_text(text):
  13. encoded_text = parse.quote_plus(text)
  14. return encoded_text.replace('+', '%20').replace('*', '%2A').replace('%7E', '~')
  15. @staticmethod
  16. def _encode_dict(dic):
  17. keys = dic.keys()
  18. dic_sorted = [(key, dic[key]) for key in sorted(keys)]
  19. encoded_text = parse.urlencode(dic_sorted)
  20. return encoded_text.replace('+', '%20').replace('*', '%2A').replace('%7E', '~')
  21. @staticmethod
  22. def create_token(access_key_id, access_key_secret):
  23. parameters = {'AccessKeyId': access_key_id,
  24. 'Action': 'CreateToken',
  25. 'Format': 'JSON',
  26. 'RegionId': 'cn-shanghai',
  27. 'SignatureMethod': 'HMAC-SHA1',
  28. 'SignatureNonce': str(uuid.uuid1()),
  29. 'SignatureVersion': '1.0',
  30. 'Timestamp': time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
  31. 'Version': '2019-02-28'}
  32. # 构造规范化的请求字符串
  33. query_string = AccessToken._encode_dict(parameters)
  34. print('规范化的请求字符串: %s' % query_string)
  35. # 构造待签名字符串
  36. string_to_sign = 'GET' + '&' + AccessToken._encode_text('/') + '&' + AccessToken._encode_text(query_string)
  37. print('待签名的字符串: %s' % string_to_sign)
  38. # 计算签名
  39. secreted_string = hmac.new(bytes(access_key_secret + '&', encoding='utf-8'),
  40. bytes(string_to_sign, encoding='utf-8'),
  41. hashlib.sha1).digest()
  42. signature = base64.b64encode(secreted_string)
  43. print('签名: %s' % signature)
  44. # 进行URL编码
  45. signature = AccessToken._encode_text(signature)
  46. print('URL编码后的签名: %s' % signature)
  47. # 调用服务
  48. full_url = 'http://nls-meta.cn-shanghai.aliyuncs.com/?Signature=%s&%s' % (signature, query_string)
  49. print('url: %s' % full_url)
  50. # 提交HTTP GET请求
  51. response = requests.get(full_url)
  52. if response.ok:
  53. root_obj = response.json()
  54. key = 'Token'
  55. if key in root_obj:
  56. token = root_obj[key]['Id']
  57. expire_time = root_obj[key]['ExpireTime']
  58. return token, expire_time
  59. print(response.text)
  60. return None, None
  61. if __name__ == "__main__":
  62. # 用户信息
  63. access_key_id = '您的AccessKeyId'
  64. access_key_secret = '您的AccessKeySecret'
  65. token, expire_time = AccessToken.create_token(access_key_id, access_key_secret)
  66. print('token: %s, expire time(s): %s' % (token, expire_time))
  67. if expire_time:
  68. print('token有效期的北京时间:%s' % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(expire_time))))