假设A开发了一系列API,要求调用这些API的调用者对调用参数进行签名,然后A在后台对每个请求的签名进行验证。
假设B要调用这些API,首先需要将参数序列化为json,然后对json string进行md5算法运算,得到签名sig
A收到B调用api的request,取出参数,记性呢md5运算,然后和sig对比。但是A取出的参数json string需要和B当时得到的一致。
可是由于json序列化可能导致json各kv对的顺序变化,这种一致性就难以保证。
另一种方法是从request请求中将字节读出来,直接用md5对字节流进行运算处理。不过这样的话,一般这个动作发生在interceptor中,字节读出来后怎么放回去让接下来的Controller读到参数呢?这又是问题。
回答
不需要对所有请求参数进行签名啊。kong网关的hmac-auth插件认证流程,感觉挺符合你的要求。他的思路是分发一个username和secret给调用者,然后调用者需要附加一些特定的请求头,并指定一个摘要方法(这个方法名称也会在header中体现)把这些请求头用密钥签名,服务器拿到请求后,根据username去查找对应的secret,并对请求的签名进行验证。
简述一下kong hmac-auth的流程
分发 username 和 secret给第三方应用
第三方请求你的api 资源,例:
POST http://example.com/restapi
构造需要签名的字符串(可以指定任何格式的字符串,只要是需要签名的字符串对双方都可见),如"", 如"date: Fri, 20 Sep 2019 22:10:02 GMT\nPOST /restapi HTTP/1.1"(使用date可以防重放攻击,统一使用gmt-0市时区)
指定签名方法,如hmac-sha1,使用分发的密钥对上述字符串提取摘要digest,并使用Base64进行转义为最终的base64_digest
signing_string="date: Fri, 20 Sep 2019 22:10:02 GMT\nPOST /restapi HTTP/1.1"
digest=HMAC-SHA1(, "secret")
base64_digest=base64()
调用方构建请求头,格式如下
curl -X POST http://example.com/restapi
-H 'Date: Fri, 20 Sep 2019 22:10:02 GMT'
-H 'Authorization: username= algorithm="hmac-sha1" signature=""'
-d '{...}'
服务器使用JAVA代码重复上面过程进行签名, 如果请求的date对比服务器时间在允许的误差内,比如说5秒内,则进行签名验证,如果大于5秒直接报错。根据请求的username在数据库中查找secret, 以同样的流程进行签名认证
1、一般是根据参数名称的ASCII码表的顺序排序,将排序好的参数名和参数值拼装在一起,然后使用MD5或者HMAC算法来签名sign
2、字节读出来后怎么放回去?–> 如果是使用spring mvc的话,用ContentCachingRequestWrapper就可以