接口签名实现
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了接口签名实现相关的知识,希望对你有一定的参考价值。
参考技术A在为第三方系统提供接口的时候,肯定要考虑接口数据的安全问题,比如数据是否被篡改,数据是否已经过时,请求是否唯一,数据是否可以重复提交等问题。其中数据是否被篡改相对重要。
请求携带参数 appid 和 sign ,只有拥有合法的身份appid和正确的签名sign才能放行。这样就解决了身份验证和参数篡改问题,即使请求参数被劫持,由于获取不到secret( 仅作本地加密使用,不参与网络传输 ),无法伪造合法的请求。
只使用appid和sign,虽然解决了请求参数被篡改的隐患,但是还存在着重复使用请求参数伪造二次请求的隐患。
nonce指 唯一的随机字符串 ,用来标识每个被签名的请求。通过为每个请求提供一个唯一的标识符,服务器能够防止请求被多次使用(记录所有用过的nonce以阻止它们被二次使用)。
然而,对服务器来说永久存储所有接收到的nonce的代价是非常大的。可以使用 timestamp来优化nonce的存储 。
假设允许客户端和服务端最多能存在10分钟的时间差,同时追踪记录在服务端的nonce集合。当有新的请求进入时,首先检查携带的timestamp是否在10分钟内,如超出时间范围,则拒绝,然后查询携带的nonce,如存在(说明该请求是第二次请求),则拒绝。否则,记录该nonce,并删除nonce集合内时间戳大于10分钟的nonce(可以使用redis的expire,新增nonce的同时设置它的超时失效时间为10分钟)。
对服务端而言,拦截请求用AOP切面或者用拦截器都行,如果要对所有请求进行拦截,可以直接拦截器处理(拦截器在切面之前,过滤器之后,具体在springmvc的dispather分发之后)。
过滤器→拦截器→切面的顺序:
其中,需要放在请求头的字段: appid 、 timestamp 、 nonce 、 signature 。
对各种类型的请求参数,先做如下拼接处理:
如果存在多种数据形式,则按照path、query、form、body的顺序进行再拼接,得到所有数据的拼接值。
上述拼接的值记作 Y。
X=”appid=xxxnonce=xxxtimestamp=xxx”
最终拼接值=XY。最后将最终拼接值按照一个加密算法得到签名。
虽然散列算法会有推荐使用 SHA-256、SHA-384、SHA-512,禁止使用 MD5。但其实签名这里用MD5加密没多大问题,不推荐MD5主要是因为,网络有大量的MD5解密库。
实现可以分以下几步:
自定义的缓存有body参数的HttpServletRequest:
过滤器中替换自定义的RequestServlet:
添加过滤器的配置以及注意顺序:
由于Zuul自带默认的过滤中,有已经对body处理过的(FormBodyWrapperFilter),所以在Zuul中处理签名,只需添加一个过滤器即可如下。
java接口签名(Signature)实现方案
开放API接口签名验证,让你的接口从此不再裸奔
JMeter BeanShell 实现接口签名验签及加解密
在利用JMeter进行接口测试或者性能测试的时候,我们需要处理一些复杂的请求,比如对接口请求参数进行签名,加密,响应数据的验签及解密,以及接口公共参数的处理,此时就需要利用BeanShell脚本了,关于BeanShell的使用小伙伴们可以查看网上相关文章。今天主要和大家分享下接口签名,验签,加解密,以及处理公共参数的例子,希望能帮助到小伙伴们。
一,思路
约定:约定接口有统一的请求及响应格式,如:
请求协议公共部分
响应协议公共部分
基于此约定,我们才能进一步统一处理。
- 引入外部签名及加解密工具包
- JMeter的HTTP请求->请求参数中只填写业务对象(parameter)
- 利用前置处理器(BeanShell PreProcessor),组装公共请求对象->对业务参数对象进行加密->签名
- 利用后置处理器(BeanShell PostProcessor)对响应报文进行验签->解密。
二,实现
整体效果如下图:
看完这篇内容后,相信以下两件事,也会对你的个人提升有所帮助:
1、 点赞,让更多人能看到这篇文章,同时你的认可也会鼓励我创作更多优质内容。
2、 让自己变得更强:想一想,如果你想在测试这个行业一直做下去,你的经验和测试技术是远远不够的,你需要进阶,你需要丰富你的技术栈!还等什么!
关于如何建立测试计划,线程组就不用一一描述了,这里只关注核心功能实现。
HTTP请求参数(parameter)部分,如:
{
"idCardNo":"511622198312241918",
"name":"Leo"
}
接口调用前置处理器-签名/加密(BeanShell PreProcessor),BeanShell代码如下:
//引入依赖
import com.javacoo.service.base.security.util.SignUtil;
import com.javacoo.service.base.security.util.SecurityUtil;
import com.javacoo.service.base.utils.WebUtil;
import com.javacoo.service.base.utils.FastJsonUtil;
import com.javacoo.service.base.BaseRequest;
import java.util.Calendar;
import java.util.Map;
import org.apache.jmeter.config.Arguments;
//开始处理
log.info("接口调用前置处理器-签名/加密相关处理");
Arguments args = sampler.getArguments();
//获取请求参数
String body = args.getArgument(0).getValue();
log.info("业务参数:{}",body);
//获取签名所需参数
String appKey = "${appKey}";
String secretkey = "${secretkey}";
String nonce = WebUtil.genTransSn();
String transactionSn = WebUtil.genTransSn();
Long timestamp = Calendar.getInstance().getTimeInMillis();
//加密
Map bodyMap = FastJsonUtil.stringToCollect(body);
log.info("params:{}",bodyMap);
for(Map.Entry entry : bodyMap.entrySet()){
entry.setValue(SecurityUtil.encryptDes(entry.getValue(),secretkey));
}
body = FastJsonUtil.toJSONString(bodyMap);
log.info("加密后业务参数:{}",body);
//签名
String sign = SignUtil.clientSign(body,nonce,timestamp.toString(),secretkey);
log.info("sign:{}",sign);
//组装接口请求对象
BaseRequest baseRequest = new BaseRequest();
baseRequest.setAppKey(appKey);
baseRequest.setNonce(nonce);
baseRequest.setTimestamp(timestamp);
baseRequest.setTransactionSn(transactionSn);
baseRequest.setSign(sign);
baseRequest.setParameter(FastJsonUtil.toBean(body));
//转换为JSON字符串
String reqBody = FastJsonUtil.toJSONString(baseRequest);
log.info("reqBody:{}",reqBody);
//重置参数值
args.getArgument(0).setValue(reqBody);
接口调用后置处理程序-验证签名/解密(BeanShell PostProcessor),BeanShell代码如下:
//引入依赖
import com.javacoo.service.base.security.util.SignUtil;
import com.javacoo.service.base.BaseResponse;
import com.javacoo.service.base.utils.FastJsonUtil;
import org.apache.commons.lang3.StringUtils;
//开始处理
log.info("接口调用后置处理器-验证签名");
String responseData = prev.getResponseDataAsString();
log.info("返回数据:{}",responseData);
BaseResponse baseResponse = FastJsonUtil.toBean(responseData, BaseResponse.class);
if(StringUtils.isBlank(baseResponse.getSign()) || baseResponse.getData().get() == null){
return;
}
//转换
String s = FastJsonUtil.toJSONString(baseResponse.getData().get());
log.info("请求返回业务json:{}",s);
log.info("请求返回签名:{}",baseResponse.getSign());
String secretkey = "${secretkey}";
//验证签名
if (SignUtil.cloudVerifySign(baseResponse.getSign(), s,baseResponse.getTransactionSn(),baseResponse.getTimestamp().toString(), secretkey)) {
log.info("返回数据合法");
} else {
log.info("返回数据被篡改");
}
//解密,TODO
三,注意事项及问题
JMeter不支持java1.5以后的语法,不支持泛型,如要使用则需要封装成JAR包。
一些信息
路漫漫其修远兮,吾将上下而求索
码云:https://gitee.com/javacoo
QQ群:902061117
作者/微信:test6738
最后:【可能给予你助力的教程】
这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。
关注我的微信公众号:【伤心的辣条】免费获取~
我的学习交流群:902061117 群里有技术大牛一起交流分享~
如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!
好文推荐:
以上是关于接口签名实现的主要内容,如果未能解决你的问题,请参考以下文章