zoukankan      html  css  js  c++  java
  • 阿里云 API 签名机制的 Python 实现

    在调用阿里云 API 的时候,最让人头疼的就是 API 的签名(Signature)机制,阿里云在通用文档中也有专项说明,但是仅仅有基于 Java 的实现代码示例。所以这里基于 Python 来分析下。

    基本步骤

    1. 构造规范化的请求字符串 (Canonicalized Query String)
    2. 构造被签名字符串 StringToSign
    3. 计算 HMAC 值
    4. 计算签名值
    5. 添加签名
      理论部分的详细内容参考阿里云官方帮助文档

    具体 Python 实现

    API 请求原理

    简单来说调用阿里云 API 就是一个 http 请求(大多数为 GET, 这里也是基于 GET 请求),只是后面要跟一堆参数,例如一个查看快照的请求为:

    http://ecs.aliyuncs.com/?SignatureVersion=1.0&Format=JSON&Timestamp=2017-08-07T05%3A50%3A57Z&RegionId=cn-hongkong&AccessKeyId=xxxxxxxxx&SignatureMethod=HMAC-SHA1&Version=2014-05-26&Signature=%2FeGgFfxxxxxtZ2w1FLt8%3D&Action=DescribeSnapshots&SignatureNonce=b5046ef2-7b2b-11e7-a3c5-00163e001831&ZoneId=cn-hongkong-b
    

    请求中所需要的公共参数(就是调用 API 都需要用到的参数)为:

    SignatureVersion # 签名算法版本,目前为 1.0 
    Format # 返回消息的格式化方式,JSON or XML 默认值为 XML
    Timestamp # 请求的时间戳,UTC时间,例如: 2013-01-10T12:00:00Z 
    AccessKeyId # 账号密钥 ID
    SignatureMethod # 签名方式,目前为 HMAC-SHA1
    Version # 版本号,为日期形式,例如: 2014-05-26 每个产品不同
    Signature # 最难搞定的签名
    SignatureNonce # 唯一随机数,防止网络攻击。不同请求间使用不同的随机数。
    

    除了 Signature 之外,其它的参数都比较容易获得,有些甚至是固定的值,具体可以参考阿里云文档

    除了公共参数之外,还需要具体接口(Action)的请求参数,每个 Action 接口的参数可以参考对应产品的接口文档,例如 DescribeLoadBalancers

    Signature 是基于公共参数和接口参数的,所以比较复杂。

    Signature 具体代码实现

    构造规范化的请求字符串 (Canonicalized Query String)

    • 构造 dict
      Python 中体现参数一一对应的就是 dict, 创建一个 dict, 把请求参数些进去,这里简化参数只有这些:
    >>> D = {
        'Format':'JSON',
        'Version':'2014-05-26',
        'SignatureMethod':'HMAC-SHA1'
        }
    
    • 排序
      由于签名要求唯一性,包括顺序,所以需要按照参数名称排序
    >>> sortedD = sorted(D.items(), key=lambda x: x[0])
    >>> sortedD
    [('Format', 'JSON'),
     ('SignatureMethod', 'HMAC-SHA1'),
     ('Version', '2014-05-26')]
    
    # 先通过 D.items() 转化为 List, 然后利用 sorted 方式按照 key 排序
    
    • URL 编码
      由于在标准请求字符串中需要使用 UTF-8 字符集,对请求参数名称和值中有些不符合规范的字符要进行 url 编码,具体规则为:

    字符 AZ、az、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 替换回波浪号(~),即可得到上述规则描述的编码字符串。

    这里使用 python 中的 urllib 库来进行编码:

    >>> def percentEncode(str):
            res = urllib.quote(str.decode(sys.stdin.encoding).encode('utf8'), '')
            res = res.replace('+', '%20')
            res = res.replace('*', '%2A')
            res = res.replace('%7E', '~')
            return res
    # 这里构造一个编码函数,对一个字符串进行编码,返回编码后的字符串    
    
    • 生成标准化请求字符串
    >>> canstring = ''
    >>> for k,v in sortedD:
            canstring += '&' + percentEncode(k) + '=' + percentEncode(v)
    >>> canstring 
    '&Format=JSON&SignatureMethod=HMAC-SHA1&Version=2014-05-26'
    

    构造被签名字符串 StringToSign

    规则为:

    StringToSign=
    HTTPMethod + “&” +
    percentEncode(“/”) + ”&” +
    percentEncode(CanonicalizedQueryString)

    所以在这个实例中

    >>> stringToSign = 'GET&%2F&' + percentEncode(canstring[1:])
    >>> stringToSign
    'GET&%2F&Format%3DJSON%26SignatureMethod%3DHMAC-SHA1%26Version%3D2014-05-26'
    # >>> percentEncode(“/”)
    # %2F
    

    计算 HMAC 值

    >>> access_key_secret = 'access_key_secret'
    >>> h = hmac.new(access_key_secret + "&", stringToSign, sha1)
    >>> h
    <hmac.HMAC instance at 0x35ed440>
    #  access_key_secret 是通过阿里云账号中的 AK 中获取的,和 access_key_id 对应,测试的时候使用的是 'access_key_secret'
    

    计算签名值

    >>> signature = base64.encodestring(h.digest()).strip()
    >>> signature
    'sq8LVH+ZItZiVQ0/rVnHV1kP/BE='
    

    到此生成了 signature 签名

    添加签名

    >>> D['Signature'] = signature
    >>> D
    {'Format': 'JSON',
     'Signature': 'sq8LVH+ZItZiVQ0/rVnHV1kP/BE=',
     'SignatureMethod': 'HMAC-SHA1',
     'Version': '2014-05-26'}
    

    所以在这个实例中,最终请求的 url 为

    >>> url = 'http://ecs.aliyuncs.com/?' + urllib.urlencode(D)
    >>> url 
    'http://ecs.aliyuncs.com/?SignatureMethod=HMAC-SHA1&Version=2014-05-26&Signature=sq8LVH%2BZItZiVQ0%2FrVnHV1kP%2FBE%3D&Format=JSON'
    

    拿到浏览器直接访问即可,得到结果为:

    {"Message":"The input parameter "Action" that is mandatory for processing this request is not supplied.","RequestId":"129880D4-710D-4D2C-9F8B-12777FA1D3C6","HostId":"ecs.aliyuncs.com","Code":"MissingParameter"}
    

    由于是测试环境,就给了三个参数,所以还少很多参数,正常来说把这些参数都加上,然后生成 signature,组成 url 后直接访问就可以得到结果。

    文档 来自:https://www.jianshu.com/p/7574349a5042



  • 相关阅读:
    bzoj2055: 80人环游世界(有源汇上下界可行最小费用流)
    bzoj千题计划158:bzoj2406: 矩阵(有源汇上下界可行流)
    bzoj1220:[HNOI2002]跳蚤
    [USACO4.3]逢低吸纳Buy Low, Buy Lower
    hdu 3488 Tour
    [网络流24题] 餐巾计划
    zkw费用流模板
    [网络流24题] 最长k可重区间集
    bzoj千题计划156:bzoj1571: [Usaco2009 Open]滑雪课Ski
    Codeforces Round #449 C. Willem, Chtholly and Seniorious (Old Driver Tree)
  • 原文地址:https://www.cnblogs.com/Qing-840/p/9300514.html
Copyright © 2011-2022 走看看