签名算法
在调用接口时需要计算当前调用参数的相关签名,并至于请求头部。
参与签名的数据如下:
- 请求头部中的AppKey(X-AK)、时间戳(X-TS)、随机数(X-NONCE)
- 请求消息中的消息体,并且key值为'body',为空则不参与签名
- 请求消息的query参数,并且key值为'params',为空则不参与签名
签名步骤
第一步:将需要签名的字段按照key1=value1&key2=value2&key3=value3的形式拼接成字符串,注意 在拼接前需要按照key的ASCII码从小到大排序(升序),key区分大小写
第二步:对第一步拼接的字符串尾部拼接提供密钥AppSecret,得到SignValue字符串
第三步:对第二步拼接得到的SignValue字符串进行md5(小写)
第四步:将第三步计算得到的签名添加至请求头中的签名中(X-SIGN)
签名实现(Postman脚本)
// 平台ak和sk
let appKey = 'BFGZRLWn'
let appSecret = '66eef434789131c8f6bae20641bb4dc0692dffd3'
// 6位随机数和时间戳
let nonce = Math.floor(Math.random() * (999999 - 100000)) + 100000;
let timestamp = Math.round(Date.now());
let API_HEADER_APPKEY = 'X-AK';
let API_HEADER_TIMESTAMP = 'X-TS';
let API_HEADER_NONCE = 'X-NONCE';
let API_HEADER_SIGN = 'X-SIGN';
// 设置请求头
pm.request.headers.add({ key: API_HEADER_APPKEY, value: appKey })
pm.request.headers.add({ key: API_HEADER_TIMESTAMP, value: timestamp })
pm.request.headers.add({ key: API_HEADER_NONCE, value: nonce })
// 签名map
const signMap = new Map();
signMap.set(API_HEADER_APPKEY, appKey);
signMap.set(API_HEADER_TIMESTAMP, timestamp);
signMap.set(API_HEADER_NONCE, nonce);
let body = pm.request.body;
if(typeof body != "undefined" && body != null && body != ""){
signMap.set('body',body);
}
let params = pm.request.url.query
if(typeof params != "undefined" && params != null && params != ""){
signMap.set('params', params);
}
// 取出key按照ASCII码从小到大排序(升序)
let keys = Array.from(signMap.keys());
keys.sort();
// 转成键值对
let linkStringArray = [];
for (let i = 0, len = keys.length; i < len; i++) {
let key = keys[i];
linkStringArray.push(key + '=' + signMap.get(key))
}
// 拼接并追加密钥
let stringSignTemp = linkStringArray.join('&') + appSecret;
console.log("signtempstring===:", stringSignTemp);
// 计算签名
let sign = CryptoJS.MD5(stringSignTemp).toString().toLowerCase();
console.log("sign====:", sign);
// 签名写入header
pm.request.headers.add({ key: API_HEADER_SIGN, value: sign })
签名实现(Java部分代码)
private HashMap<String, String> createSignMap(HttpServletRequest request) {
HashMap<String, String> signMap = new HashMap<>();
// 请求头
signMap.put(OpenApiHeader.APPKEY, HttpUtil.getHeader(request, OpenApiHeader.APPKEY));
signMap.put(OpenApiHeader.TIMESTAMP, HttpUtil.getHeader(request, OpenApiHeader.TIMESTAMP));
signMap.put(OpenApiHeader.NONCE, HttpUtil.getHeader(request, OpenApiHeader.NONCE));
RequestReaderHttpServletRequestWrapper requestWrapper = (RequestReaderHttpServletRequestWrapper) request; ;
String body = requestWrapper.getBodyConent();
if (StrUtil.isNotBlank(body)) {
signMap.put("body", body);
}
// query参数
// 中文的查询参数在http请求时会进行urlencode编码后进行传递,在获取时特需要解码拿到最原始的数据
String queryString = request.getQueryString();
String params = URLDecoder.decode(queryString, StandardCharsets.UTF_8);
if (StrUtil.isNotBlank(params)) {
signMap.put("params", params);
}
return signMap;
}
private String calculateSign(HashMap<String, String> signMap, String appSecret) {
StringBuilder toSignBuilder = new StringBuilder();
List<String> keys = signMap.keySet().stream().sorted().collect(Collectors.toList());
for (int i = 0; i < keys.size(); i++) {
if (i != 0) {
toSignBuilder.append("&");
}
String key = keys.get(i);
toSignBuilder.append(key);
toSignBuilder.append("=");
toSignBuilder.append(signMap.get(key));
}
// 最后追加盐
toSignBuilder.append(appSecret);
String signString = toSignBuilder.toString();
log.info("待签名字符串:" + signString);
return MD5.create().digestHex(signString);
}