API密钥签名认证详解,包含timestamp+nonce方案BY:Zz Apollo

1 篇文章 0 订阅
1 篇文章 0 订阅

本文举例来说明API签名,并有具体实现流程,规则弄会,一通百通。
本文先用一个故事举例,方便理解,然后对整个流程做了逐步分析和局部代码实现,最后把代码整合起来,想直接看整合后代码的可以直接去最底。

一、故事引入签名认证原理(不要纠结为什么吃饭这么麻烦- -!)

        有家饭店,只对会员开放,小明在饭店注册会员。
        饭店给小明一个会员号"xiaoming"(签名中的ApiKey)并且永久有效。
        又给小明一个令牌(签名中的SecretKey),令牌内容是"abcd",并告诉小明令牌只能自己知道,不可以告诉别人,又告知令牌30天后失效,失效后可以再来申请一个新的令牌(依然30天有效期)。
        (饭店是知道小明的会员号和令牌的)
        这天,小明要在饭店点餐,主食:米饭,炒菜:土豆丝, 于是给饭店发送了点菜短信,短信内容可以有多种方案,具体内容如下(这里只写两个短信方案):
        方案一:

会员号:xiaoming
主食:米饭
炒菜:土豆丝
口令(signature):abcd(实际开发中,令牌abcd不会用于网络通讯中,
			而是把会员号,主食,炒菜三个键值对加密拼接,
			并用‘abcd’进行加密,
			具体实现下面会说明,这里先了解流程)

        方案二:

会员号:小明
主食:米饭
炒菜:土豆丝
时间(timestamp):12点(假设偏差10分钟,如果饭店收到消息比11点50早
			或比12点10分晚,就认为是骗子)
随机码(nonce):7878(饭店每次都记录随机码,收到信息后看看随机码之前用过没,
			如果用过,就认为是骗子)
口令(signature):abcd(实际开发中,令牌abcd不会用于网络通讯中,
			而是会把上边5个键值对先加密,
			再拼接,再用‘abcd’加密作为口令,
			具体实现下面会说明,这里先了解流程)

        饭店收到信息后核对无误,开始做饭。
        有人会有疑问,如果骗子随便写个随机码,刚好之前没用过,岂不是就蒙混过关了??注意一点,口令的生成和随机码有关系,所以只要骗子拿不到令牌(secretkey),就无法生成正确的口令。

二、准备(用户注册申请密钥)

        服务器会返回两个key

AccessKey: 身份标识,唯一,类似ID,用户名什么的
SecretKey:密钥,只保存在客户端和服务器,不做网络传递,一般都有有效期

本文中

accesskey="ch_improve"
secretkey="abcd"

三、客户端生成签名

1.准备参数

        需要传递的参数有:

{
	"accesskey": "ch_improve",
	"name": "gaofushuai""city": "zhengzhou",
}

        实际运用中,可以添加参数,例如

SignatureMethod="HmacSHA256"
timestamp=1572662767
method="POST"
nonce=7878

        我以如下参数为例

{
	"accesskey": "ch_improve",
	"name": "gaofushuai",
	"city": "zhengzhou",
	"SignatureMethod": "HmacSHA256",
	"timestamp": 1572662767,
	"method": "POST",
	"nonce": 7878
}
2.对参数进行排序并转为URL键值对格式字符串

        首先需要对传递的参数按照键的字典升序进行排序,并得到URL键值对格式的字符串
SignatureMethod=HmacSHA256&accesskey=ch_improve&city=zhengzhou&method=POST&name=gaofushuai&nonce=7878&timestamp=1572662767

SignatureMethod=HmacSHA256&accesskey=ch_improve&city=zhengzhou&method=POST&name=gaofushuai&nonce=7878&timestamp=1572662767

        python3代码实现
                (1)字典升序排序

args = {
    "accesskey": "ch_improve",
    "name": "gaofushuai",
    "city": "zhengzhou",
    "SignatureMethod": "HmacSHA256",
    "timestamp": 1572662767,
    "method": "POST",
    "nonce": 7878
}

args_order = sorted(args.items(), key=lambda x: x[0])

                args_order的值如下

[('SignatureMethod', 'HmacSHA256'), ('accesskey', 'ch_improve'), ('city', 'zhengzhou'), ('method', 'POST'), ('name', 'gaofushuai'), ('nonce', 7878), ('timestamp', 1572662767)]

                (2)转为URL字符串

import urllib.parse

args_str = urllib.parse.urlencode(args_order)

                args_str的值如下,是一个长字符串

SignatureMethod=HmacSHA256&accesskey=ch_improve&city=zhengzhou&method=POST&name=gaofushuai&nonce=7878&timestamp=1572662767
3.使用密钥SecretKey对URL格式的字符串args_str进行加密获取签名

        常用加密方法有MD5、SHA256等等,本文采用HmacSha256加密

import hmac
secretkey="abcd"
# 先把secretkey和args_str转为byte类型
b_secretkey = secretkey.encode('utf-8')
b_args_str = args_str.encode('utf-8')
# 用密钥对URL格式的参数进行hmacsha256加密
signature = hmac.new(b_secretkey, b_args_str, digestmod='sha256').hexdigest()

        signature签名的值如下

3623d8070662392772d10952ea39c277518546006e6649f4ea358671e51e06f7

        有些服务器要求把signature签名字符串全部转为大写,转不转大写看接口要求,本文就不再转大写了

4.在原有参数基础上添加signature签名参数,得到最新的参数
{
	"accesskey": "ch_improve",
	"name": "gaofushuai",
	"city": "zhengzhou",
	"SignatureMethod": "HmacSHA256",
	"timestamp": 1572662767,
	"method": "POST",
	"nonce": 7878,
	"signature": "3623d8070662392772d10952ea39c277518546006e6649f4ea358671e51e06f7"
}
5.得到最终参数后,可以发送请求了,至于如何携带参数,基本有三种选择(URL字符串、请求头、请求体)

        (1)URL键值对字符串

accesskey=ch_improve&name=gaofushuai&city=zhengzhou&
SignatureMethod=HmacSHA256&timestamp=1572662767&meth
od=POST&nonce=7878&signature=3623d8070662392772d10952
ea39c277518546006e6649f4ea358671e51e06f7

                比如访问http://127.0.0.1:8888,那么url就是

http://127.0.0.1:8888/?accesskey=ch_improve&name=gaofushuai
&city=zhengzhou&SignatureMethod=HmacSHA256&timestamp=1572662767
&method=POST&nonce=7878&signature=3623d8070662392772d10952e
a39c277518546006e6649f4ea358671e51e06f7

        (2)请求头中携带参数
                可以自定义一个字段,比如"MyArgs",值就使用字典参数的json格式字符串,如下

Myargs: {"accesskey": "ch_improve", "name": "gaofushuai", "city": "zhengzhou", "SignatureMethod": "HmacSHA256", "timestamp": 1572662767, "method": "POST", "nonce": 7878, "signature": "3623d8070662392772d10952ea39c277518546006e6649f4ea358671e51e06f7"}

        (3)请求体中携带参数
                这个直接放入请求体就行,作为一般参数

四、服务器收到请求后验证

1.接受参数
{
	"accesskey": "ch_improve",
	"name": "gaofushuai",
	"city": "zhengzhou",
	"SignatureMethod": "HmacSHA256",
	"timestamp": 1572662767,
	"method": "POST",
	"nonce": 7878,
	"signature": "3623d8070662392772d10952ea39c277518546006e6649f4ea358671e51e06f7"
}
2.检查时间戳timestamp(如果不使用时间戳作为参数,则跳过此步)

        假如约定时间戳误差范围为10分钟,也就是600秒,根据接收到的时间戳1572662767计算出时间戳范围

1572662767-600  ~~  1572662767+600

        获取服务器本地时间戳

import time
timestamp = time.time()

        如果得到结果不在范围内,则拒绝访问

3.检查随机动态码nonce是否有效(如果不使用nonce作为参数,则跳过此步)

        (1)nonce为客户端随机生成的验证码,当服务器接收到请求后,会把nonce存储到数据库中,一般使用redis,并设置一个有效期,一般和时间戳timestamp的失效时间保持一致,设为10分钟有效期。
        (2)当服务器接收到请求后,用请求中nonce(本文为7878)和redis中的nonce集合做比较,如果已经存在,则拒绝访问接口,只有当10分钟之内第一次使用7878这个动态码,才判定访问有效。

4.验证签名signature

        对除了signature之外的所有参数进行升序URL格式转换拼接,得到字符串再用密钥secretkey(本文是‘abcd’)加密,得到signature2,和请求中的signature做比较判断请求是否合法。
        不一致则拒绝访问。

五、总结下生成签名认证请求参数生成的代码

import json  # 导入json
import urllib.parse  # 导入URL编译库
import hmac  # 导入HMAC加密库

accesskey="ch_improve"
# 密钥,仅保存在客户端和服务端,不做网络传递,一般都有有效期,15天之类的
secretkey="abcd"

# 准备参数
# 时间戳timestamp可以用 timestamp = int(time.time()) 生成
# nonce是随机生成的验证码,例如"7878", "k9i8e7"
args = {
    "accesskey": "ch_improve",
    "name": "gaofushuai",
    "city": "zhengzhou",
    "SignatureMethod": "HmacSHA256",
    "timestamp": 1572662767,
    "method": "POST",
    "nonce": 7878
}

# 对参数进行排序,按照键的字典升序排序
args_order = sorted(args.items(), key=lambda x: x[0])
# 得到args_order=[('SignatureMethod', 'HmacSHA256'), ('accesskey', 'ch_improve'), ('city', 'zhengzhou'), ('method', 'POST'), ('name', 'gaofushuai'), ('nonce', 7878), ('timestamp', 1572662767)]

# 对排好序的元祖列表进行URL转换
args_str = urllib.parse.urlencode(args_order)
# 得到字符串SignatureMethod=HmacSHA256&accesskey=ch_improve&city=zhengzhou&method=POST&name=gaofushuai&nonce=7878&timestamp=1572662767

# 对密钥和字符串进行byte转换
b_secretkey = secretkey.encode('utf-8')
b_args_str = args_str.encode('utf-8')

# 用密钥abcd进行加密
signature = hmac.new(b_secretkey, b_args_str, digestmod='sha256').hexdigest()
# 得到签名3623d8070662392772d10952ea39c277518546006e6649f4ea358671e51e06f7

# 把签名添加到请求参数中作为新的请求参数
{
    "accesskey": "ch_improve",
    "name": "gaofushuai",
    "city": "zhengzhou",
    "SignatureMethod": "HmacSHA256",
    "timestamp": 1572662767,
    "method": "POST",
    "nonce": 7878"signature": "3623d8070662392772d10952ea39c277518546006e6649f4ea358671e51e06f7"
}

最后希望大家给个赞吧

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值