访问令牌(Access Token)是调用智能语音交互服务的凭证。我们提供了基于阿里云公共SDK调用云端服务获取Access Token的方式。本文为您介绍获取Access Token的接口协议说明。
请求报文
客户端向服务端发送获取Token的请求,服务端返回创建Token结果的响应。客户端发送的请求支持使用HTTP/HTTPS协议,请求方法支持GET/POST方法。服务端提供了基于阿里云POP协议的接口,因此客户端需要实现阿里云POP的签名机制。
由于HTTPS协议的请求参数设置与HTTP协议相同,下面以HTTP协议请求为例,介绍如何发送请求获取Token。
URL
协议
URL
方法
HTTP/1.1
http://nlsmeta.ap-southeast-1.aliyuncs.com/
GET/POST
请求参数
名称
类型
是否必选
说明
AccessKeyId
String
是
阿里云账号AccessKey ID
Action
String
是
POP API名称:CreateToken
Version
String
是
POP API版本:2019-02-28
Format
String
是
响应返回的类型:JSON
RegionId
String
是
服务所在的地域ID:ap-southeast-1
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,用于请求的防重放攻击,每次请求唯一,不能重复使用。格式为A-B-C-D-E(A、B、C、D、E的字符位数分别为8、4、4、4、12)。例如,8d1e6a7a-f44e-40d5-aedb-fe4a1c80f434。
Signature
String
是
由所有请求参数计算出的签名结果,生成方法参见下文签名机制。
说明使用GET方法,需要将请求参数设置在请求行中:
/?请求参数字符串
。使用POST方法,需要将请求参数设置在请求体中。
HTTP请求头部
HTTP请求头部由“关键字-值”对组成,每行一对,关键字和值用英文冒号“:”分隔,内容如下:
名称
类型
是否必选
描述
Host
String
否
HTTP请求的服务器域名:
nlsmeta.ap-southeast-1.aliyuncs.com
,一般根据请求链接自动解析。Accept
String
否
指定客户端能够接收的内容类型:
application/json
,不设置默认为 */* 。Content-type
String
POST方法必须设置
指定POST方法请求体数据格式:
application/x-www-form-urlencoded
。
报文示例:
HTTP GET请求报文
GET /?Signature=O0s6pfeOxtFM6YKSZKQdSyPR9Vs%3D&AccessKeyId=LTAF3sAA****&Action=CreateToken&Format=JSON&RegionId=ap-southeast-1&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 Host: nlsmeta.ap-southeast-1.aliyuncs.com User-Agent: curl/7.49.1 Accept: */*
HTTP POST请求报文
POST / HTTP/1.1 Host: nlsmeta.ap-southeast-1.aliyuncs.com User-Agent: curl/7.49.1 Accept: */* Content-type: application/x-www-form-urlencoded Content-Length: 276 SignatureVersion=1.0&Action=CreateToken&Format=JSON&SignatureNonce=8d1e6a7a-f44e-40d5-aedb-fe4a1c80f434&Version=2019-02-28&AccessKeyId=LTAF3sAA****&Signature=oT8A8RgvFE1tMD%2B3hDbGuoMQSi8%3D&SignatureMethod=HMAC-SHA1&RegionId=ap-southeast-1&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在该时间之前有效。)。
HTTP/1.1 200 OK Date: Mon, 25 Mar 2019 09:29:24 GMT Content-Type: application/json; charset=UTF-8 Content-Length: 216 Connection: keep-alive Access-Control-Allow-Origin: * Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-Requested-With, X-Sequence, _aop_secret, _aop_signature Access-Control-Max-Age: 172800 Server: Jetty(7.2.2.v20101205) {"NlsRequestId":"dd05a301b40441c99a2671905325****","RequestId":"E11F2DC2-0163-4D97-A704-0BD28045****","ErrMsg":"","Token":{"ExpireTime":1553592564,"Id":"88916699****","UserId":"150151111111****"}}
响应体JSON字符串内容如下。
{ "NlsRequestId": "dd05a301b40441c99a2671905325****", "RequestId": "E11F2DC2-0163-4D97-A704-0BD28045****", "ErrMsg": "", "Token": { "ExpireTime": 1553592564, "Id": "889******166", "UserId": "150**********151" } }
失败响应
HTTP状态码为非200,响应字段说明如下表。
参数
类型
说明
RequestId
String
请求ID
Message
String
失败响应的错误信息
Code
String
失败响应的错误码
说明请根据错误码和错误信息提示检查请求参数是否设置正确,如无法排查,请您提交工单并提供响应信息,咨询产品技术支持。
以重传入阿里云账号的AccessKey Id错误为例,响应消息如下。
HTTP/1.1 404 Not Found Date: Thu, 28 Mar 2019 07:23:01 GMT Content-Type: application/json; charset=UTF-8 Content-Length: 290 Connection: keep-alive Access-Control-Allow-Origin: * Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-Requested-With, X-Sequence, _aop_secret, _aop_signature Access-Control-Max-Age: 172800 Server: Jetty(7.2.2.v20101205) {"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-CD4365C2****","HostId":"nlsmeta.ap-southeast-1.aliyuncs.com","Code":"InvalidAccessKeyId.NotFound"}
响应体JSON字符串内容如下。
{ "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-CD4365C2****", "HostId": "nlsmeta.ap-southeast-1.aliyuncs.com", "Code": "InvalidAccessKeyId.NotFound" }
签名机制
服务端POP API对每个接口访问请求的发送者都要进行身份验证,所以无论使用HTTP协议还是HTTPS协议提交的请求,都需要在请求中包含签名信息。通过签名机制,服务端可以确认哪位用户在做API请求,并能确认请求在网络传输过程中是否被篡改。
安全验证流程:
计算签名时,需要阿里云账号的AccessKey Id和AccessKey Secret,使用HMAC-SHA1算法进行对称加密。其工作流程如下。
请求端根据API请求内容(包括HTTP请求参数和请求体)生成签名字符串。
请求端使用阿里云账号的AccessKey Id和AccessKey Secret对第一步生成的签名字符串进行签名,获得该API请求的数字签名。
请求端把API请求内容和数字签名一同发送给服务端。
服务端在接收到请求后会重复1、2步骤(服务端会在后台获取该请求使用的用户密钥)并计算出该请求期望的数字签名。
服务端用期望的数字签名和请求端发送过来的数字签名做对比,若完全一致则认为该请求通过验证。否则拒绝该请求。
生成请求的签名字符串:
构造规范化的请求字符串。
将HTTP请求参数(不包括Signature)构造成规范化的请求字符串。规范化步骤:
参数排序。按参数名的字典顺序,对请求参数进行大小写敏感排序。
参数编码。对排序后的请求参数进行规范化设置。
请求参数名和值都要使用
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
替换为波浪号~
,即可得到上述规则描述的编码字符串。使用等号
=
连接URL编码后的参数名和参数值:percentEncode(参数Key) + “=” + percentEncode(参数值)
。使用与(
&
)号连接第c步URL编码后的请求参数对,如Action=CreateToken&Format=JSON
。说明字符串中第一个参数名前面不需要
&
符号。返回规范化的请求字符串。
示例如下:
String percentEncode(String value) throws UnsupportedEncodingException { return value != null ? URLEncoder.encode(value, URL_ENCODING) .replace("+", "%20") .replace("*", "%2A") .replace("%7E", "~") : null; } // 对参数Key排序 String[] sortedKeys = queryParamsMap.keySet().toArray(new String[] {}); Arrays.sort(sortedKeys); // 对排序的参数进行编码、拼接 for (String key : sortedKeys) { canonicalizedQueryString.append("&") .append(percentEncode(key)).append("=") .append(percentEncode(queryParamsMap.get(key))); } queryString = canonicalizedQueryString.toString().substring(1);
说明完整代码实现请参见完整示例的
canonicalizedQuery
函数。构造规范化的请求字符串:
AccessKeyId=LTA******3s2&Action=CreateToken&Format=JSON&RegionId=ap-southeast-1&SignatureMethod=HMAC-SHA1&SignatureNonce=f20b1beb-e5dc-4245-9e96-aa582e905c1a&SignatureVersion=1.0&Timestamp=2019-04-03T03%3A40%3A13Z&Version=2019-02-28
构造待签名字符串。
将HTTP请求的方法(GET)、URL编码的URL路径(/)、第1步获取的规范化请求字符串使用与(
&
)符号连接成待签名字符串:HTTPMethod + "&" + percentEncode("/") + "&" + percentEncode(queryString)
。构造签名字符串代码示例:
StringBuilder strBuilderSign = new StringBuilder(); strBuilderSign.append(HTTPMethod); strBuilderSign.append("&"); strBuilderSign.append(percentEncode(urlPath)); strBuilderSign.append("&"); strBuilderSign.append(percentEncode(queryString)); stringToSign = strBuilderSign.toString();
说明完整代码实现请参见完整示例的
createStringToSign
函数。构造的签名字符串:
计算签名。
签名采用HMAC-SHA1算法 + Base64,编码采用
UTF-8
。根据AccessKey Secret,将第2步构造的待签名字符串使用HMAC-SHA1算法计算出对应的数字签名。其中,计算签名时使用的AccessKey Secret必须在其后面增加一个与字符
&
。签名也要做URL编码。
计算签名的代码示例:
signature = Base64( HMAC-SHA1(stringToSign, accessKeySecret + "&") ); // 进行URL编码 signature = percentEncode(signature)
说明完整代码实现请阅读完整示例的
sign
函数。计算得到的签名:
# 签名串 AKIktdPUMCV12fTh667BLXeuCtg= # URL编码后的结果 AKIktdPUMCV12fTh667BLXeuCtg%3D
计算签名后,将签名的键值对用符号
=
连接,并使用符号&
添加到第1步获取的请求字符串中,作为HTTP GET请求参数,发送到服务端,获取Token。String queryStringWithSign = "Signature=" + signature + "&" + queryString;
快速测试
您可以使用以下参数计算签名,比较计算结果是否一致。
AccessKey Id和AccessKey Secret没有提供真实数据,Timestamp是过期的时间,使用这些参数计算的签名,获取Token时会失败,仅用于计算签名测试对比。
AccessKeyId:
my_access_key_id
AccessKeySecret:
my_access_key_secret
Timestamp:
2019-04-18T08:32:31Z
SignatureNonce:
b924c8c3-6d03-4c5d-ad36-d984d3116788
请求参数如下:
AccessKeyId:my_access_key_id
Action:CreateToken
Version:2019-02-28
Timestamp:2019-04-18T08:32:31Z
Format:JSON
RegionId:ap-southeast-1
SignatureMethod:HMAC-SHA1
SignatureVersion:1.0
SignatureNonce:b924c8c3-6d03-4c5d-ad36-d984d3116788
规范化请求字符串。
AccessKeyId=my_access_key_id&Action=CreateToken&Format=JSON&RegionId=ap-southeast-1&SignatureMethod=HMAC-SHA1&SignatureNonce=b924c8c3-6d03-4c5d-ad36-d984d3116788&SignatureVersion=1.0&Timestamp=2019-04-18T08%3A32%3A31Z&Version=2019-02-28
构造待签名字符串。
计算得到签名。
hHq4yNsPitlfDJ2L0nQPdugdEzM= # URL编码后的结果 hHq4yNsPitlfDJ2L0nQPdugdEzM%3D
得到携带签名的请求字符串。
Signature=hHq4yNsPitlfDJ2L0nQPdugdEzM%3D&AccessKeyId=my_access_key_id&Action=CreateToken&Format=JSON&RegionId=ap-southeast-1&SignatureMethod=HMAC-SHA1&SignatureNonce=b924c8c3-6d03-4c5d-ad36-d984d3116788&SignatureVersion=1.0&Timestamp=2019-04-18T08%3A32%3A31Z&Version=2019-02-28
组合成HTTP请求链接。
http://nlsmeta.ap-southeast-1.aliyuncs.com/?Signature=hHq4yNsPitlfDJ2L0nQPdugdEzM%3D&AccessKeyId=my_access_key_id&Action=CreateToken&Format=JSON&RegionId=ap-southeast-1&SignatureMethod=HMAC-SHA1&SignatureNonce=b924c8c3-6d03-4c5d-ad36-d984d3116788&SignatureVersion=1.0&Timestamp=2019-04-18T08%3A32%3A31Z&Version=2019-02-28
替换第5步获取的HTTP请求链接,使用浏览器或者curl,获取Token。
curl "http://nlsmeta.ap-southeast-1.aliyuncs.com/?Signature=${您的签名}&AccessKeyId=${您的AccessKey Id}&Action=CreateToken&Format=JSON&RegionId=ap-southeast-1&SignatureMethod=HMAC-SHA1&SignatureNonce=${您的请求UUID}&SignatureVersion=1.0&Timestamp=${您的请求时间戳}&Version=2019-02-28"
完整示例
本文提供Java、Python语言示例代码,您可根据协议和示例实现其他语言的客户端程序。
Java示例
依赖文件:
<!-- http://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.9.1</version> </dependency>
示例代码:
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.SimpleTimeZone; import java.util.UUID; public class CreateToken { private final static String TIME_ZONE = "GMT"; private final static String FORMAT_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss'Z'"; private final static String URL_ENCODING = "UTF-8"; private static final String ALGORITHM_NAME = "HmacSHA1"; private static final String ENCODING = "UTF-8"; private static String token = null; private static long expireTime = 0; /** * 获取时间戳 * 必须符合ISO8601规范,并需要使用UTC时间,时区为+0。 */ public static String getISO8601Time(Date date) { Date nowDate = date; if (null == date) { nowDate = new Date(); } SimpleDateFormat df = new SimpleDateFormat(FORMAT_ISO8601); df.setTimeZone(new SimpleTimeZone(0, TIME_ZONE)); return df.format(nowDate); } /** * 获取UUID */ public static String getUniqueNonce() { UUID uuid = UUID.randomUUID(); return uuid.toString(); } /** * URL编码 * 使用UTF-8字符集按照RFC3986规则编码请求参数和参数取值。 */ public static String percentEncode(String value) throws UnsupportedEncodingException { return value != null ? URLEncoder.encode(value, URL_ENCODING).replace("+", "%20") .replace("*", "%2A").replace("%7E", "~") : null; } /*** * 将参数排序后,进行规范化设置,组合成请求字符串。 * @param queryParamsMap 所有请求参数 * @return 规范化的请求字符串 */ public static String canonicalizedQuery( Map<String, String> queryParamsMap) { String[] sortedKeys = queryParamsMap.keySet().toArray(new String[] {}); Arrays.sort(sortedKeys); String queryString = null; try { StringBuilder canonicalizedQueryString = new StringBuilder(); for (String key : sortedKeys) { canonicalizedQueryString.append("&") .append(percentEncode(key)).append("=") .append(percentEncode(queryParamsMap.get(key))); } queryString = canonicalizedQueryString.toString().substring(1); System.out.println("规范化后的请求参数串:" + queryString); } catch (UnsupportedEncodingException e) { System.out.println("UTF-8 encoding is not supported."); e.printStackTrace(); } return queryString; } /*** * 构造签名字符串 * @param method HTTP请求的方法 * @param urlPath HTTP请求的资源路径 * @param queryString 规范化的请求字符串 * @return 签名字符串 */ public static String createStringToSign(String method, String urlPath, String queryString) { String stringToSign = null; try { StringBuilder strBuilderSign = new StringBuilder(); strBuilderSign.append(method); strBuilderSign.append("&"); strBuilderSign.append(percentEncode(urlPath)); strBuilderSign.append("&"); strBuilderSign.append(percentEncode(queryString)); stringToSign = strBuilderSign.toString(); System.out.println("构造的签名字符串:" + stringToSign); } catch (UnsupportedEncodingException e) { System.out.println("UTF-8 encoding is not supported."); e.printStackTrace(); } return stringToSign; } /*** * 计算签名 * @param stringToSign 签名字符串 * @param accessKeySecret 阿里云AccessKey Secret加上与号& * @return 计算得到的签名 */ public static String sign(String stringToSign, String accessKeySecret) { try { Mac mac = Mac.getInstance(ALGORITHM_NAME); mac.init(new SecretKeySpec( accessKeySecret.getBytes(ENCODING), ALGORITHM_NAME )); byte[] signData = mac.doFinal(stringToSign.getBytes(ENCODING)); String signBase64 = DatatypeConverter.printBase64Binary(signData); System.out.println("计算的得到的签名:" + signBase64); String signUrlEncode = percentEncode(signBase64); System.out.println("UrlEncode编码后的签名:" + signUrlEncode); return signUrlEncode; } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(e.toString()); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException(e.toString()); } catch (InvalidKeyException e) { throw new IllegalArgumentException(e.toString()); } } /*** * 发送HTTP GET请求,获取token和有效期时间戳。 * @param queryString 请求参数 */ public static void processGETRequest(String queryString) { /** * 设置HTTP GET请求 * 1. 使用HTTP协议 * 2. Token服务域名:nlsmeta.ap-southeast-1.aliyuncs.com * 3. 请求路径:/ * 4. 设置请求参数 */ String url = "http://nlsmeta.ap-southeast-1.aliyuncs.com"; url = url + "/"; url = url + "?" + queryString; System.out.println("HTTP请求链接:" + url); Request request = new Request.Builder() .url(url) .header("Accept", "application/json") .get() .build(); try { OkHttpClient client = new OkHttpClient(); Response response = client.newCall(request).execute(); String result = response.body().string(); if (response.isSuccessful()) { JSONObject rootObj = JSON.parseObject(result); JSONObject tokenObj = rootObj.getJSONObject("Token"); if (tokenObj != null) { token = tokenObj.getString("Id"); expireTime = tokenObj.getLongValue("ExpireTime"); } else{ System.err.println("提交获取Token请求失败: " + result); } } else { System.err.println("提交获取Token请求失败: " + result); } response.close(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String args[]) { if (args.length < 2) { System.err.println("CreateTokenDemo need params: <AccessKey Id> <AccessKey Secret>"); System.exit(-1); } String accessKeyId = args[0]; String accessKeySecret = args[1]; System.out.println(getISO8601Time(null)); // 所有请求参数 Map<String, String> queryParamsMap = new HashMap<String, String>(); queryParamsMap.put("AccessKeyId", accessKeyId); queryParamsMap.put("Action", "CreateToken"); queryParamsMap.put("Version", "2019-02-28"); queryParamsMap.put("Timestamp", getISO8601Time(null)); queryParamsMap.put("Format", "JSON"); queryParamsMap.put("RegionId", "ap-southeast-1"); queryParamsMap.put("SignatureMethod", "HMAC-SHA1"); queryParamsMap.put("SignatureVersion", "1.0"); queryParamsMap.put("SignatureNonce", getUniqueNonce()); /** * 1.构造规范化的请求字符串 */ String queryString = canonicalizedQuery(queryParamsMap); if (null == queryString) { System.out.println("构造规范化的请求字符串失败!"); return; } /** * 2.构造签名字符串 */ String method = "GET"; // 发送请求的 HTTP 方法,GET String urlPath = "/"; // 请求路径 String stringToSign = createStringToSign(method, urlPath, queryString); if (null == stringToSign) { System.out.println("构造签名字符串失败"); return; } /** * 3.计算签名 */ String signature = sign(stringToSign, accessKeySecret + "&"); if (null == signature) { System.out.println("计算签名失败!"); return; } /** * 4.将签名加入到第1步获取的请求字符串 */ String queryStringWithSign = "Signature=" + signature + "&" + queryString; System.out.println("带有签名的请求字符串:" + queryStringWithSign); /** * 5.发送HTTP GET请求,获取token。 */ processGETRequest(queryStringWithSign); if (token != null) { System.out.println("获取的Token:" + token + ", 有效期时间戳(秒):" + expireTime); // 将10位数的时间戳转换为北京时间 String expireDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(expireTime * 1000)); System.out.println("Token有效期的北京时间:" + expireDate); } } }
Python示例
说明Python版本要求:Python3.4及以上。
安装Python HTTP库Requests:
pip install requests
#!/usr/bin/env python # -*- coding: utf-8 -*- import base64 import hashlib import hmac import requests import time import uuid from urllib import parse class AccessToken: @staticmethod def _encode_text(text): encoded_text = parse.quote_plus(text) return encoded_text.replace('+', '%20').replace('*', '%2A').replace('%7E', '~') @staticmethod def _encode_dict(dic): keys = dic.keys() dic_sorted = [(key, dic[key]) for key in sorted(keys)] encoded_text = parse.urlencode(dic_sorted) return encoded_text.replace('+', '%20').replace('*', '%2A').replace('%7E', '~') @staticmethod def create_token(access_key_id, access_key_secret): parameters = {'AccessKeyId': access_key_id, 'Action': 'CreateToken', 'Format': 'JSON', 'RegionId': 'ap-southeast-1', 'SignatureMethod': 'HMAC-SHA1', 'SignatureNonce': str(uuid.uuid1()), 'SignatureVersion': '1.0', 'Timestamp': time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), 'Version': '2019-02-28'} # 构造规范化的请求字符串 query_string = AccessToken._encode_dict(parameters) print('规范化的请求字符串: %s' % query_string) # 构造待签名字符串 string_to_sign = 'GET' + '&' + AccessToken._encode_text('/') + '&' + AccessToken._encode_text(query_string) print('待签名的字符串: %s' % string_to_sign) # 计算签名 secreted_string = hmac.new(bytes(access_key_secret + '&', encoding='utf-8'), bytes(string_to_sign, encoding='utf-8'), hashlib.sha1).digest() signature = base64.b64encode(secreted_string) print('签名: %s' % signature) # 进行URL编码 signature = AccessToken._encode_text(signature) print('URL编码后的签名: %s' % signature) # 调用服务 full_url = 'http://nlsmeta.ap-southeast-1.aliyuncs.com/?Signature=%s&%s' % (signature, query_string) print('url: %s' % full_url) # 提交HTTP GET请求 response = requests.get(full_url) if response.ok: root_obj = response.json() key = 'Token' if key in root_obj: token = root_obj[key]['Id'] expire_time = root_obj[key]['ExpireTime'] return token, expire_time print(response.text) return None, None if __name__ == "__main__": # 用户信息 access_key_id = '您的AccessKeyId' access_key_secret = '您的AccessKeySecret' token, expire_time = AccessToken.create_token(access_key_id, access_key_secret) print('token: %s, expire time(s): %s' % (token, expire_time)) if expire_time: print('token有效期的北京时间:%s' % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(expire_time))))