遇到的问题
以前工作中写脚本访问阿里云的资源时,都是通过调用系统命令(调用CLI工具)的方式,这次尝试通过http请求来实现想要的操作。
本次实现中遇到的问题:
1、向api发送请求是总是返回报错:Specified parameter Version is not valid,从报错上看是参数中设置的Version的值有问题,但反复核实请求中带的Version并没有问题。
解决方法:如果你遇到同样的问题,估计跟我一样,构建好请求url后,在向api发送请求时也是以调用系统命令(curl)的方式来实现的,这里要把请求的url用引号引起来,形式如下:cmd = "curl ‘http://xxx.example.com?Aciton=XXXX&…’ ",具体为什,据说是因为url中的&符号,是一个命令行执行后台挂起的标志,导致curl命令执行与预期不符,我并没有去验证,大体上应该就是这么回事了
2、在解决了上一个问题后,再次发送请求,返回结果为:errorCode:IncompleteSignature,msg:“The request signature does not conform to Aliyun standards.”
解决方法:报错很明显是计算得到的签名Signature有问题,问题出在待签名字符串上,待签名字符串中的参数部分,我只对它进行了一次urlencode操作,还需要再进行一次percentEncode操作,将其中的等号“=”,编码成%3D
签名代码实现
下面记录一下代码的实现,学过的东西还是要多复习,毕竟编码经验还不纯熟。(我安装的是python3.7的版本)
用法:aliyunTool.py -f params.json
params.json示例:
{
"endpoint": "https://ecs.aliyuncs.com",
"AccessKeyId": "xxxxxxxxxxxxxxxxxxxxxxx",
"AccessKeySecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Params": {
"AccessKeyId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Format": "json",
"Version": "2014-05-26",
"SignatureMethod": "HMAC-SHA1",
"Timestamp": "",
"SignatureVersion": "1.0",
"SignatureNonce": "",
"Action": "DescribeInstances",
"RegionId": "cn-beijing",
"PageSize": "50"
}
}
代码示例,复制可直接使用:
import datetime
import urllib.request
import urllib.parse
import hmac
import base64
import json
import uuid
import argparse
import copy
def getConfigFromFile(configFile):
config_file = open(configFile)
config = json.load(config_file)
config_file.close()
# 计算时间戳参数,使用的是utc时间,并格式化成指定的格式
return config
def requestByConfig(config):
#计算时间戳参数,使用的是utc时间,并格式化成指定的格式
timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
#下面三个参数要求全局唯一,使用uuid生成随机字符串
#ClientToken = str(uuid.uuid4()) #本示例中用不到
#Token = str(uuid.uuid4()) #本示例中用不到
signatureNonce = str(uuid.uuid4())
#下面根据阿里云文档计算待签名字符串
#def generateToSignStr(commonparams,customparams):
#首先合并公共参数和自定义参数,然后排序
params = copy.copy(config["Params"])
params["Timestamp"] = timestamp
params["SignatureNonce"] = signatureNonce
sortedParams = sorted(params.items(), key=lambda x: x[0])
#然后对合并排序后的参数进行urlencode编码,得到的是多个key=value的键值对通过&符号连接后组成的字符串
urlencodeParams = urllib.parse.urlencode(sortedParams).replace("+","%20").replace("*","%2A").replace("%7E","~")
#再处理一次,将urlencode后的字符串中的“=”和“&”进行percent编码
urlencodeParams = urllib.parse.quote_plus(urlencodeParams)
#最后生成待签名字符串
toSignStr = "GET"+"&"+urllib.parse.quote_plus("/")+"&"+urlencodeParams
#计算签名
h = hmac.new((config["AccessKeySecret"]+"&").encode(),toSignStr.encode(),"sha1")
signature = base64.encodebytes(h.digest()).strip().decode()
#将Signature添加到请求参数,生成请求url
params["Signature"] = signature
url = config["endpoint"]+"?"+urllib.parse.urlencode(params).replace("+","%20")
print(url)
#发送请求并打印结果
res=None
try:
response = urllib.request.urlopen(url)
# 先将结果转换成字典,再转换成json对象,格式化输出
res = json.load(response)
# print(json.dumps(res, sort_keys=True, indent=2))
return res
except IOError as e:
print(e.read())
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--conf", type=str, default="config.json", help="请求接口的配置及参数的配置文件")
args = parser.parse_args()
conf = getConfigFromFile(args.conf)
x = requestByConfig(conf)
print(x)
params = {
"endpoint": "https://ecs.aliyuncs.com",
"AccessKeyId": "xxxxxxxxxxxxxxxxxxxxxxx",
"AccessKeySecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Params": {
"AccessKeyId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Format": "json",
"Version": "2014-05-26",
"SignatureMethod": "HMAC-SHA1",
"Timestamp": "",
"SignatureVersion": "1.0",
"SignatureNonce": "",
"Action": "DescribeInstances",
"RegionId": "cn-beijing",
"PageSize": "50"
}
}
y = requestByConfig(params)
print(y)