签名生成
<h2>签名生成</h2>
<p>商家可以按照下述步骤生成请求的签名。在本节的最后,我们准备了java语言的演示代码供开发者参考。</p>
<p>平台要求商家对请求进行签名。平台会在收到请求后进行签名的验证。如果签名验证不通过,平台将会拒绝处理请求。</p>
<h3>构造签名串</h3>
<ol>
<li>
<p>将鉴权头部信息(排除<code>sign</code>属性,并忽略空值,如果signType不传值则不加入签名串中)以键值对的方式,按属性名字母升序后用<code>&</code>拼接:</p>
<pre><code>businessId=754ea3e3a699404f9b82e8f881107f19&nonce=24bc2c17-0fef-440e-ba32-385c47c6cfa4&signType=MD5&timestamp=1628772138668</code></pre>
</li>
<li>
<p>将请求体转为JSON串(字段不分前后),拼接到步骤1后,生成待签名串:</p>
<pre><code class="language-java"> /**
* 构建待签名串
* @param headerParams 鉴权头数据(不含sign)
* @param dataJson 业务数据的JSON串
* @return 待签名串
*/
private String getSignString(Map<String, String> headerParams, String dataJson) {
String headerString = headerParams.keySet().stream()
.sorted()
.map(key -> key + "=" + headerParams.get(key))
.collect(Collectors.joining("&"));
return headerString + dataJson;
}</code></pre>
<p>结果:</p>
<pre><code>businessId=754ea3e3a699404f9b82e8f881107f19&nonce=24bc2c17-0fef-440e-ba32-385c47c6cfa4&signType=MD5&timestamp=1628772138668{"businessId":"754ea3e3a699404f9b82e8f881107f19","merchantId":"eb1bb305ca3940f8adf1d4e964555699"}</code></pre>
</li>
</ol>
<h3>计算签名值</h3>
<p>把<strong>密钥</strong>追加到待签名串最后,进行<strong>MD5</strong>签名,并对签名结果进行<strong>转换全部大写</strong>得到签名值</p>
<pre><code class="language-java"> /**
* 数字签名-MD5
* @param content 待签名内容
* @param signKey 签名key
* @return 签名串
*/
public static String signWithMD5(String content, String signKey) throws Exception {
return Md5Utils.digest(String.format("%s%s", content, signKey)).toUpperCase();
}</code></pre>
<h3>设置HTTP头</h3>
<p>将签名和鉴权头部信息放入HTTP请求头中</p>
<h3>演示代码</h3>
<pre><code class="language-java"> @Autowired
RestTemplate restTemplate;
/**
* 签名并发送请求
* @param businessId 业务ID
* @param signKey 签名key
* @param api API接口
* @param data 业务数据
* @param <T>
* @return 响应体
*/
private <T> ResponseEntity<String> exchangeWithSign(String businessId, String signKey, String api, T data) {
// 头部参数
Map<String, String> headerParams = generatorHeaderParams(businessId);
// 签名
String dataJson = JSON.toJSONString(data);
String signString = getSignString(headerParams, dataJson);
log.info("待签名字符串:{}", signString);
// 使用商户私钥签名
try {
String sign = signWithMD5(signString, signKey);
headerParams.put("sign", sign);
} catch (Exception e) {
e.printStackTrace();
}
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("http://127.0.0.1:8080/")
.path(api)
.build(true);
URI uri = uriComponents.toUri();
// 设置请求头信息
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
headers.setContentType(MediaType.parseMediaType("application/json;charset=UTF-8"));
// 将认证信息加入请求头
headerParams.forEach(headers::add);
RequestEntity<String> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(dataJson);
return restTemplate.exchange(requestEntity, String.class);
}
/**
* 构建鉴权头部数据
* @param businessId
* @return
*/
private Map<String, String> generatorHeaderParams(String businessId) {
Map<String, String> headerParams = new HashMap<>();
headerParams.put("businessId", businessId);
headerParams.put("nonce", UUID.randomUUID().toString());
headerParams.put("timestamp", String.valueOf(System.currentTimeMillis()));
headerParams.put("signType", "MD5");
return headerParams;
}
/**
* 构建待签名串
* @param headerParams 鉴权头数据
* @param dataJson 业务数据的JSON串
* @return 待签名串
*/
private String getSignString(Map<String, String> headerParams, String dataJson) {
String headerString = headerParams.keySet().stream()
.sorted()
.map(key -> key + "=" + headerParams.get(key))
.collect(Collectors.joining("&"));
return headerString + dataJson;
}
/**
* 数字签名-signWithMD5
* @param content 待签名内容
* @param signKey 签名用的私钥字符串-Base64编码后的
* @return 签名串
* @throws Exception
*/
public static String signWithMD5(String content, String signKey) throws Exception {
return Md5Utils.digest(String.format("%s%s", content, signKey)).toUpperCase();
}</code></pre>
<p>Md5Utils:</p>
<pre><code class="language-java">
import org.springframework.util.DigestUtils;
/**
* Md5加密方法
*
*/
public class Md5Utils {
public static String digest(String s) {
return DigestUtils.md5DigestAsHex(s.getBytes());
}
}</code></pre>