最近工作中在测试WebApi的时候遇到了鉴权,验证的场景,涉及到获取token,加密验签等问题。
主要使用python的Hashlib库和request库,前者可以处理信息加密和摘要,后者完成Http收发。
首先介绍一下这段时间学习下来自己对于加密加签的理解: 对称加密(AES):用同一段密文进行加密解密,得到相同的值 非对称加密(RSA):双方各执一对密钥,私钥和公钥,然后双方互换公钥:本地留存客户端私钥和服务器公钥,服务器留存服务器私钥和客户端公钥,发送时使用私钥加密,接收方用发送方的公钥解密,返回报文使用接收方私钥加密,发送方收到后使用接收方的公钥解密。此类加密方式多用于验签。
在此总结一下几个常用请求参数的生成代码:
一. Nonce&Uuid生成
由于两者生成方法近似,故放在一起说, Nonce:number once,即只被使用一次的随机整数。 Uuid:Universally Unique Identifier,全局唯一标识符 两者都是随机数,但是用处不同。
import hashlib
def
getNonce():
#根据当前时间生成随机戳
md5str
= hashlib
.md5()
md5str
.update((str(int(time
.time() * 1000)) + str(random
.random
)).encode())
nonce
= md5str
.hexdigest()
return nonce
def
getRandomUuid(length
):
#生成指定长度的UUID,也可以使用randomuuid库生成
i
= 0
charList
= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
uuid
= ""
while(i
<length
):
uuid
+= charList
[random
.randint(0,61)]
i
+= 1
return uuid
二.MD5生成
MD5: MessageDigest 消息摘要
md5
= hashlib
.md5()
md5
.update(####KEY####
)
result
= md5
.hexdigest()
三.获取Token
此过程就是向指定token服务器发送个人信息然后获取返回的token。
def
getToken(token_url
):
randomUuid
= getRandomUuid(32)
md5
= hashlib
.md5()
md5
.update((appId
+ appSecret
+ randomUuid
).encode("utf-8"))
SignatureStr
= md5
.hexdigest()
req
= \
{
xxxx
: xxxx #视token接口而定
"randomUuid": randomUuid
,
"SignatureStr": SignatureStr
,
}
try:
res
= requests
.post(token_url
, json
=req
)
except
:
return "token got error , please ensure the network situation."
print(res
.status_code
)
token
= res
.json()['Token'] #视token接口而定
print('token: ' + token
)
return token
四.加密加签解密验签
这几个功能如果要实现必须是本地加密加签逻辑与发送方服务器完全一致,我遇到的情况是:后台服务是springcloud,用的是Keygenerate库使用SecureRandom生成唯一随机数,用这个随机数来SHA256加密的方法来加签,这种情况下,python的随机数无法实现这样的要求,因此产生的结果就是:python加签验签自成了一套体系,我能validate自己的sign,但是与服务器无法结合。(如果服务端加密逻辑简单的话就可以实现) 实现博文地址: Python3.7基于hashlib和Crypto实现加签验签功能
因此,针对这个方法,此处提出一种解决思路,即复用java的加密逻辑。另提供两种解决办法: 第一种方法使用JPYPE库调jar包的class。 第二种方法起一个本地加密服务来返回签名和加密数据。
方法1实现步骤:
组jar包,包含sign,validate,encrypt,decrypt等class代码中调用:
import JPype
jpath
= JPype
.getDefaultJVMPath()#取系统默认JVM路径
JPype
.JVMStart(jpath
)
Jsign
= JPype
.Jclass('com.xxx')#从jar包里找要的
class
sign = Jsign(data
,privateKey
)
.....
方法2实现步骤:
新建一个springboot项目新建一个controller来路由请求和服务函数 下面例子中函数中固定了默认的私钥公钥路径,因此不做传入。
package com
.XXX
.controller
;
import com
.XXX
.SourceCode
.*
;
import org
.springframework
.web
.bind
.annotation
.GetMapping
;
import org
.springframework
.web
.bind
.annotation
.RequestParam
;
import org
.springframework
.web
.bind
.annotation
.RestController
;
@RestController
public class XXXcontroller {
@GetMapping("/info")
public String
welcomeService(){
return "welcome to XXX service.";
}
@GetMapping("/encrypt")
public String
encryptService(@RequestParam(name
= "data") String data
,@RequestParam(name
= "key") String key
){
String encryptedData
= "";
try {
encryptedData
= XXX
.encrypt(data
, key
);
}catch (Exception e
){System
.out
.println(e
);return e
.getMessage();}
return encryptedData
;
}
@GetMapping("/decrypt")
public String
decryptService(@RequestParam(name
= "data") String data
,@RequestParam(name
= "key") String key
){
String decryptedData
= "";
try {
decryptedData
= XXX
.decrypt(data
, key
);
}catch (Exception e
){System
.out
.println(e
);return e
.getMessage();}
return decryptedData
;
}
@GetMapping("/sign")
public String
signService(@RequestParam(name
= "data") String data
){
String sign
= "";
try{
sign
= XXX
.sign(data
);
}catch (Exception e
){return e
.getMessage();}
return sign
;
};
@GetMapping("/validate")
public String
validateService(@RequestParam(name
= "sign") String sign
,@RequestParam(name
= "data") String data
){
String validate
= "";
try{
validate
= XXX
.validate(sign
,data
);
}catch (Exception e
){return e
.getMessage();}
return validate
;
};
}
运行服务http请求访问服务
实现了加密和鉴权的功能之后再做自动化的接口测试就水到渠成了。