写python http的sdk遇到的坑(单引号和双引号)

故事背景:项目要开放一下api给客户使用,接口都ready了,文档也写好了;对于open api,最基础的一项就是鉴权问题,如果鉴权ok了,对接工作可以稳步进行。同事把鉴权逻辑的文档写好了,但是客户发现用python写的接口总是报401,为了手把手帮客户解决问题,三年没再写python的我上场了。

签名与鉴权

签名逻辑:客户端把请求数据序列化后,转成bytes,然后用密钥加密,把签名放到http请求的header里。

鉴权逻辑:服务端从http请求的header中取出签名和请求数据,本地进行签名,对比两个签名结果是否一致。

代码

我的第一版鉴权代码如下:

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import hmac, base64, hashlib, requests, json

class Api_Auth(object):
	def __init__(self):
     # replace following params with your ak sk
		self.ak = 'ak'
		self.sk = bytes('sk', encoding='utf-8')
		self.host = ""

	def getSignKey(self, data):
		sign = hmac.new(self.sk, data, hashlib.sha1).digest()
		val = self.ak + ":" + base64.urlsafe_b64encode(sign).decode()
		return val

	def getRequestData(self, method, path, queryStr="", bodyStr="", contentType="application/json"):
		data = method+" "+path
		if queryStr != "":
			data += "?"+queryStr
		data += "\nHost: "+self.host
		if bodyStr != "":
			data += "\nContent-Type: " + contentType
		data += "\n\n"
		if bodyStr != "" and bodyStr != "application/octet-stream":
			data += bodyStr
		return data

# get status
class Api_Data(object):
	def get_data(self):
		url = ''
		path = '/v1/status'
		data = Api_Auth().getRequestData("GET", path, "", "")
		signkey = Api_Auth().getSignKey(data.encode('utf-8'))
		headers = {
			'Authorization':signkey,
		}
		response = requests.get(url, headers=headers, data='')
		return response.content.decode()

print(Api_Data().get_data())

测试了一个get请求,正常返回结果,完美!于是就把这个demo发布出去了。

然而!后面有用户反馈接口还是401,不过不是get接口,是post接口,我于是开始编写post接口并测试,代码如下

def post_data(self,jsonData):
	url = ''
	path = ''
	data = Api_Auth().getRequestData("POST", path, "", str(jsonData))
	signkey = Api_Auth().getSignKey(data.encode('utf-8'))
	headers = {
	    'Authorization':signkey,
	}
	print(signkey)

测试发现确实不通;就觉得挺奇怪的,然后把签名前的data打印出来,用python签名代码和golang签名版本做对照(因为项目的server是用golang鉴权的),两者数据如下:

POST /v1/task
Host: aaa.com
Content-Type: application/json

{'name': 'test'}
POST /v1/task
Host: aaa.com
Content-Type: application/json

{"name":"test"}

当时我一看结果是一样的,python的字符串也支持单引号表示,然后就对比长度,又对比签名后base64之前和之后的bytes,发现确实不一致;又对比签名前data的bytes数据,发现只有少数几个地方不一致,ascii码分别为39和34,查ascii码表发现分别是单引号和双引号。这时候发现不对了,其实前面打印的结果就不一致,但是由于python的特性让我忽略了,序列后的结果应该完全一致才行,但为啥json的就是单引号呢。

结论

网上查完发现,对于一个dict,直接str转换是可以得到相应的字符串的,dict里的key或者字符串类型的value都是加单引号的,而对于golang的web接口,字符串是必须用双引号括起来的,所以解决方法是将dict序列化后的结果都带双引号,即使用json.dumps,并且在调用python的request.post方法时,传入的数据也必须是json.dumps转换后的结果。

更正后的代码为

def post_data(self,jsonData):
	url = ''
	path = ''
	data = Api_Auth().getRequestData("POST", path, "", json.dumps(jsonData))
	signkey = Api_Auth().getSignKey(data.encode('utf-8'))
	headers = {
	    'Authorization':signkey,
	}
	print(signkey)

最后对比下str和json.dumps的区别

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值