签名规则
1.使用URL键值对的格式(即key1=value1&key2=value2…)参数名ASCII码从小到大排序(字典序)拼接成字符串stringA;
2.在stringA最后拼接上company_secret得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue;
3.传参的字段sign是通过签名规则生成的,但不参加签名计算;company_secret参加签名计算,但不在传参字段之列。
举例说明
以网络货运的【车辆备案】接口为例,假设要传的参数为
content:01,04,4403162320,33903671,1165.05,20170803,81171643890998027896,27E4
company_key:44167fc5-c8e9-4ba0-9224-656345f26d5b
department_id:a013476188ce4bcb99b1edb0ed73361f
nonce_str:123456
sign:(完成签名计算后填入)
company_secret为
company_secret:f21e6d76-b47e-4c62-96d1-63a19a5f4116
第一步,给传参字段排序拼接(不含sign)
content=01,04,4403162320,33903671,1165.05,20170803,81171643890998027896,27E4&company_key=44167fc5-c8e9-4ba0-9224-656345f26d5b&department_id=a013476188ce4bcb99b1edb0ed73361f&nonce_str=123456
第二步,在第一步拼接结果的最后拼接上company_secret
content=01,04,4403162320,33903671,1165.05,20170803,81171643890998027896,27E4&company_key=44167fc5-c8e9-4ba0-9224-656345f26d5b&department_id=a013476188ce4bcb99b1edb0ed73361f&nonce_str=123456&company_secret=f21e6d76-b47e-4c62-96d1-63a19a5f4116
第三步,使用MD5为第二步拼接结果的字符串加密,取32位大写的值,作为sign的值
9212B21EE89BBCE83A1CFD2753093516
第四步,最终需要传参的字段key:value即为
content:01,04,4403162320,33903671,1165.05,20170803,81171643890998027896,27E4
company_key:44167fc5-c8e9-4ba0-9224-656345f26d5b
department_id:a013476188ce4bcb99b1edb0ed73361f
nonce_str:123456
sign:9212B21EE89BBCE83A1CFD2753093516
假设要传的参数为:
content = "01,04,4403162320,33903671,1165.05,20170803,81171643890998027896,27E4,"
company_key = "26bbab36-8c2d-44c3-a7fd-2ec6a5d423c7"
nonce_str = "000000"(6位随机字符串)
用于加密的company_secret为:
company_secret = "5a35328a-15ba-4f0b-b32c-afe56c6589c7"
第一步:排序为:stringA = company_key=26bbab36-8c2d-44c3-a7fd-2ec6a5d423c7&content=01,04,4403162320,33903671,1165.05,20170803,81171643890998027896,27E4,&nonce_str=000000
第二步:拼接:stringSignTemp=stringA + “&company_secret=5a35328a-15ba-4f0b-b32c-afe56c6589c7",md5加密后再转换为大写,最终结果为:
FD4667ABF01B264278586E3C15FDF96C
Python 示例代码
def sign_action(key, parameters):
sortedParameters = sorted(
parameters.items(), key=lambda parameters: parameters[0])
canonicalizedQueryString = ''
for (k, v) in sortedParameters:
if v or v == 0:
canonicalizedQueryString += '&' + str(k).strip() + '=' + str(v).strip()
tempstring = canonicalizedQueryString[1:] + '&' + 'company_secret=' + str(key)
signature = md5(tempstring.strip().encode()).hexdigest().upper()
print(tempstring)
print(signature)
return signature
Java 示例代码
private static Object invokeFee(@NotNull Map<String, String> kvMap,
@NotNull Map<String, Object> ignore,//不参与加密数据
@NotNull String url,
int method) {
kvMap.put("company_key", AppPropertyUtils.getKey());
//6位随机字符串
kvMap.put("nonce_str", AppUitls.randomStr(6));
Map<String, Object> resultMap = new HashMap<>();
ArrayList<String> keys = Lists.newArrayList(kvMap.keySet());
Collections.sort(keys, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
/*
* *按ASCII表排序
* *可以看到return有个? :表达式。
* *解释一下
* *o1.compareToIgnoreCase(o2) == 0是先看看不区分大小写对比出来的值是否等于0,
* *如果=0,返回-o1.compareTo(o2),前面加-是让小写在大写前面
* *如果不等于0,返回o1.compareToIgnoreCase(o2)
*/
return (o1.compareToIgnoreCase(o2) == 0 ? -o1.compareTo(o2) : o1.compareToIgnoreCase(o2));
}
});
try {
StringBuilder params = new StringBuilder();
for (String key : keys) {
params.append(key);
params.append("=");
params.append(kvMap.get(key));
params.append("&");
resultMap.put(key, kvMap.get(key).toString());
}
params.append(URLEncoder.encode("company_secret", "utf-8"));
params.append("=");
params.append(URLEncoder.encode(AppPropertyUtils.getSecret(), "utf-8"));
logger.info("参数排序后为: " + params.toString());
String md5 = Md5.getEncode(params.toString());
resultMap.put("sign", md5);
logger.info("======>>加密后签名: " + md5);
} catch (Exception e) {
logger.error("======>>签名失败:", e);
return ErrorResps.SIGN_ERROR.get();
}
if (ignore != null) resultMap.putAll(ignore);
Object content = null;
try {
if(method==HTTP_GET){
String entity = HttpHelper.doGetEntity(url, resultMap);
content = new JacksonJsonParser().parseMap(entity);
}else{
ResponseContent postEntity = HttpHelper.postEntity(url, resultMap);
content = new JacksonJsonParser().parseMap(postEntity.getContent());
}
logger.info("feecloud reponse: " +content);
} catch (Exception e) {
logger.error("invoke feecloud failed: ", e);
return ErrorResps.INVOKE_FEECLOUD_FAILED.get();
}
logger.info("======费耘返回数据: " + content);
return content;
}