PYTHON 实现 SIGN 签名: 用于提供给外部(第三方) 做系统对接



简介

sign 签名是用于提供给外部(第三方)调用的接口,调用方需要提供正确的 appkey 钥匙才能调用,确保了接口的安全性。

前言

在 app 开放接口 api 的设计中,避免不了的就是安全性问题,因为大多数接口涉及到用户的个人信息以及一些敏感的数据,所以对这些接口需要进行身份的认证,那么这就需要用户提供一些信息,比如用户名密码等,但是为了安全起见让用户暴露的明文密码次数越少越好,我们一般在 web 项目中,大多数采用保存的 session 中,然后在存一份到 cookie 中,来保持用户的回话有效性。但是在 app 提供的开放接口中,后端服务器在用户登录后如何去验证和维护用户的登陆有效性呢,以下是参考项目中设计的解决方案,其原理和大多数开放接口安全验证一样,如淘宝的开放接口 token 验证,微信开发平台 token 验证都是同理。

接口安全等级

       完全开放的接口, 没有任何验证

       接口参数加密(签名)

       接口参数加密+时效性验证

       接口参数加密+时效性验证+私钥

       接口参数加密+时效性验证+私钥+Https

签名设计

对于敏感的 api 接口,需使用 https 协议

           https 是在 http 超文本传输协议加入 SSL 层,它在网络间通信是加密的,所以需要加密证书。

           https 协议需要 ca 证书,一般需要交费。


sign 签名算法

 签名算法规则:

  • 第一步,设所有发送或者接收到的数据为集合 M,将集合 M 内非空参数值的参数按照参数名 ASCII 码从小到大排序(字典序),使用 URL 键值对的格式(即 key1=value1&key2=value2…)拼接成字符串 stringA。
  • 第二步,在 stringA 最后拼接上 KEY 得到 stringSignTemp 字符串(即stringSignTemp = stringA + &key=KEY),并对 stringSignTemp 进行 MD5 运算,再将得到的字符串所有字符转换为大写,得到 sign 值 signValue

用于签名的密钥 KEY 值为 keysecret

注意事项:

  • a、参数名 ASCII 码从小到大排序(字典序);
  • b、如果参数的值为空(即 null 或空字符串)不参与签名;
  • c、参数名区分大小写;
  • d、验证签名时,传送的 sign 参数不参与签名,将生成的签名与该 sign 值作校验;
  • e、接口可能增加字段,验证签名时必须支持增加的扩展字段;





签名参数 sign 生成方法

假设有请求参数如下:

appkey = "111222333"
body = {
    "username": "Test",
    "Password": "123456",
    "mail": "",
    "sign": "xxx"
}


第一步

将所有参数(注意是所有参数),除去 sign 本身,以及值为空的参数,转化为键值对,没有等于号的字符串。

期望的结果如下:

["usernameTest", "Password123456"]


代码实现 2 种方式:

i[1] 指的是字典的值,i[0] 指的是字典的键。判断条件:如果值不等于空,而且键不等于 "sign"。则条件成立,for 循环遍历,往 list 列表里面添加 i,join 函数把所有取出来的 i,连接在一起。

1:for 循环实现:

l = []
for i in body.items():
    if i[1] != "" and i[0] != "sign":
        l.append("".join(i))
print(l) # ["usernameTest", "Password123456"]

2:列表生成式实现:

s = ["".join(i) for i in body.items() if i[1] != "" and i[0] != "sign"]
print(s) # ["usernameTest", "Password123456"]


第二步

排序后的参数按照参数 1 值 1,参数 2 值 2 的键值对顺序拼接成一个字符串,按参数名字母顺序升序排序。(具体升降顺序得问开发,一般为升序)

期望的结果如下:(按字母顺序:Password 开头的在 username 前面)

“Password123456usernameTest”

代码实现:

# TODO 按字母升序排序
sort = "".join(sorted(list))
print(sort)

实际结果:

'Password123456usernameTest'


第三步

在前面得到的字符串后面,加上接入方验证密匙 appkey。

期望结果:

Password123456usernameTest111222333


代码实现:

# todo 3:在第二步得到的字符串后面,加上接入方验证密匙key,然后计算md5值,
result = sort+appkey
print(result)

实际结果:

'Password123456usernameTest111222333'


第四步

然后将这个字符串换为小写进行 md5 加密计算,得到的这个值即为 sign 签名值。

注意,计算 md5 之前请确保接口与接入方的字符串编码一致,如统一使用 utf-8 编码或者 GBK 编码,如果编码方式不一致则计算出来的签名会校验失败。

代码实现:

# todo MD5 加密,固定的写法
def jiami(params):
    m = hashlib.md5()
    m.update(params.encode("utf-8"))
    return m.hexdigest()

sign = jiami(result.lower())   #lower()把字符转为小写
print(sign)

实际结果:

'11b07f381c4ff5d7cd736af94519db2b'


全部过程如下

'''sign 签名主要是用于提供给外部(第三方)调用的接口, 需要提供 appkey 钥匙才能调用'''
import hashlib

appkey = "111222333"
body = {
    "username": "Test",
    "Password": "123456",
    "mail": "",
    "sign": "xxx"
}
# todo 第1步: 将所有参数(注意是所有参数),除去 sign 本身,以及值是空的参数,转化为键值对的

#s = ["".join(i) for i in body.items() if i[1] != "" and i[0] != "sign"]
#print(s)

list = []
for i in body.items():
    if i[1] != "" and i[0] != "sign":
        list.append("".join(i))
print(list)


# todo 2:排序后的参数按照参数1值1,参数2值2的键值对顺序拼接成一个字符串,按参数名字母升序排序
# TODO 按字母升序排序
sort = "".join(sorted(list))
print(sort)

# todo 3:在第二步得到的字符串后面,加上接入方验证密匙key,然后计算md5值,
result = sort+appkey
print(result)

# todo MD5加密,固定的写法
def jiami(params):
    m = hashlib.md5()
    m.update(params.encode("utf-8"))
    return m.hexdigest()

sign = jiami(result.lower())   #lower()把字符转为小写
print(sign)


 封装后的


import hashlib

def get_sign(body, appkey="111222333"):
    list = []
    for i in body.items():
        if i[1] != "" and i[0] != "sign":
            list.append("".join(i))
    sort = "".join(sorted(list))
    result = sort + appkey
    return result.lower()

def jiami(params):
    m = hashlib.md5()
    m.update(params.encode("utf-8"))
    return m.hexdigest()

body = {
    "username": "Test",
    "Password": "123456",
    "mail": "",
    "sign": "xxx"
}
#print(body.get("sign"))  # todo get取字典的值不会报异常
#print(body["username"])
sign = jiami(get_sign(body))   #lower()把字符转为小写
print(sign)


python 接口自动化:对外接口 sign 签名

签名参数 sign 生成的方法:

  在接口开发过程中,一般通过时间戳 + sign 作为密匙加密传输

实现代码如下:

# python 实现 sign 签名

import hashlib,time

class sign:
    def get_time(self):
        t1=time.time()
        t=int(t1)
        return t

    def get_str(self, t, apikey):
        st = str(t)
        c=st + apikey
        return c

    def get_md5(self, c):
        md5=hashlib.md5()
        md5.update(c.encode('UTF-8'))
        m=md5.hexdigest()
        return m

    def get_sign(self, apikey, body):
        s=sign()
        t=s.get_time()
        stt=s.get_str(t, apikey)
        m=s.get_md5(stt)
        body['sign']=m
        return body

if __name__ == '__main__':
    # 验证密钥,由开发提供
    apikey="12345678"
    body={'username': 'swust','password': 'A123456','mail': '','sign': ''}
    bb=sign().get_sign(apikey, body)
    print(bb)