互联网并不是一个十分安全的网络环境,针对一些关键API,如支付、借贷等API,为了防止数据泄漏与篡改,需要使用数据加密与签名的机制。
签名机制
对所发送的内容进行加签(签名),而接受者需要对内容进行验签(签名),证明内容没有被篡改,以此来保障开发者应用和开放平台交互的安全性。
一个完整的签名请求流程如下:

请求结构
Request Structure

Request URL
请求地址为https://{domainName}/api/{version}/{restfulPath}。
domainName:Superapp开放平台服务域名。
restfulPath:OpenAPI接口路径,例如:
/v1/authorizations/applyToken、/v1/users/inquiryUserInfo。version:OpenAPI版本。
Request Method
仅使用POST请求。
Request Header
请求头主要包含以下几个Fields(Field是大小写敏感的)。
Header | Required | Description | Code sample |
X-Request-Sign | Yes | 本次请求生成的签名,默认为HmacSHA1。 | X-Request-Sign:**** |
X-Access-Key | Yes | 请求方的身份识别ID,AccessKey。 | X-Access-Key:颁发给用户的AccessKey。 |
X-Request-Timestamp | Yes | 时间戳 | X-Timestamp:时间戳。 |
Content-Type | No | 内容格式 | Content-Type: application/json; charset=UTF-8 |
Request Body
Body为JSON格式的详细请求信息,其中的字段因接口而异。
Response Structure

Response Body
Response Body包含返回给客户端的业务信息,同时也包含请求的结果信息resultCode、resultMessage。
Field | Data Type | Required | Description |
resultCode | String | No | 结果代码. 最大长度:64个字符。 |
resultMessage | String | No | 结果代码的详细描述。 最大长度:256个字符。 |
加签/验签
1. 加签请求
首先需要构造用于加签的content,每次请求都会有以下属性:
HTTP_URI : 例如 /v1/authorizations/applyToken
X-Access-Key:46b1cac78ed94ca4b99ad6de550a****
X-Request-Timestamp:1676904384074
HTTP_BODY : body 举例如下:
{ "appId": "223232323****", "authClientId": "7230000****, "grantType": "authorization_code", "authCode": "1cc19911172e4f8aaa509c8fb5d1****" }
基于以上信息,
{Content_To_Be_Signed}的格式如下,我们可以构造出需要加签的content。<HTTP_METHOD> <HTTP_URI> <Client-Id>.<Request-Time>.<Access-Key>POST /v1/authorizations/applyToken { "appId": "223232323****", "authClientId": "7230000****", "grantType": "authorization_code", "authCode": "1cc19911172e4f8aaa509c8fb5d1****" }.1676904384074.46b1cac78ed94ca4b99ad6de550afb68使用HmacSHA1算法,以下的代码示例展示了如何加签一个请求。
/** * * @param requestURI * @param clientId * @param requestTime * @param privateKey * @param requestBody * @return */ public static String sign(String requestURI, String accessKey, String secretKey, Long requestTime, String requestBody) { String content = String.format("POST %s\n%s.%s.%s", requestURI, requestBody, requestTime, accessKey); String signed = generateSign(content, secretKey, "HmacSHA1"); return Base64.getEncoder().encodeToString(signed.getBytes(StandardCharsets.UTF_8)); } public static String generateSign(String content, String key, String algorithm) { if (!StringUtils.isEmpty(algorithm) && !StringUtils.isEmpty(content) && null != key) { try { SecretKeySpec signinKey = new SecretKeySpec(key.getBytes(), algorithm); Mac mac = Mac.getInstance(algorithm); mac.init(signinKey); byte[] rawHmac = mac.doFinal(content.getBytes()); return convertToHex(rawHmac); } catch (InvalidKeyException | NoSuchAlgorithmException var6) { return ""; } } else { return ""; } }
2.发送请求
通过将Client-Id,、Request-Time和Signature添加到请求头,可以构造出如下请求:
curl -X POST \
https://example.com/v1/authorizations/applyToken \
-H 'Content-Type: application/json' \
-H 'X-Access-Key: 46b1cac78ed94ca4b99ad6de550afb68' \
-H 'X-Request-Timestamp: 1676904384074' \
-H 'Signature: KrwDE9tAPJYBb4cUZU6ALJxGIZgwDXn5UkFPMip09n%2FkYKPhEIII%2Fki2rYY2lPtuKVgMNz%2BtuCU%2FjzRpohDbrOd8zYriiukpGAxBQDIVbatGI7WYOcc9YVQwdCR6ROuRQvr%2FD1AfdhHd6waAASu5Xugow9w1OW7Ti93LTd0tcyEWQYd2S7c3A73sHOJNYl8DC1PjasiBozZ%2FADgb7ONsqHo%2B8fKHsLygX9cuMkQYTGIRBQsvfgICnJhh%2BzXV8AQoecJBTrv6p%xxxx' \
-d '{
"appId": "223232323****",
"authClientId": "7230000****",
"grantType": "authorization_code",
"authCode": "1cc19911172e4f8aaa509c8fb5d1****"
}'3. 验签返回
当收到返回时,客户端需要对返回做验签处理。通常返回包含response header与response body两部分。举例如下:
response header
Client-Id: TEST_3990000000x
Response-Time: 2022-12-19T12:12:12+08:00
Signature: algorithm=RSA256, keyVersion=0, signature=p9T2hXxIjek0UOLw3fwlthNsV6ATaioIvu8X1uFx8a9tE87d2XEhqylnf0KjifJ3WhCoMokl
GwwlDS3tsSenwnL0Ha6BsXbJvUHRC5qcVlNy5Oq%2FpNqx2%2BKdwbw4eY7tZBDQhMKoaMVSbqbCb3eRBxxxxresponse body
{
"resultCode":"SUCCESS",
"resultMessage":"success"
"accessToken": "201509BBeff9351ad1874306903e96b91d248A36",
"refreshToken": "201509BBdcba1e3347de4e75ba3fed2c9abebE36",
"tokenExpiryTime": "2022-12-15T12:12:12+08:00",
"refreshTokenExpiryTime": "2022-12-30T12:12:12+08:00"
}基于以上信息,可以构造出需要加签的content如下:
POST /v1/authorizations/applyToken
TEST_3990000000x.2022-12-19T12:12:12+08:00.{
"resultCode":"SUCCESS",
"resultMessage":"success"
"accessToken": "201509BBeff9351ad1874306903e96b91d248A36",
"refreshToken": "201509BBdcba1e3347de4e75ba3fed2c9abebE36",
"tokenExpiryTime": "2022-12-15T12:12:12+08:00",
"refreshTokenExpiryTime": "2022-12-30T12:12:12+08:00"
}
验签demo代码如下:
/**
*
* @param requestURI
* @param clientId
* @param reponseTime
* @param publicKey
* @param responseBody
* @param signatureToBeVerified
* @return
*/
public static boolean verify(String requestURI, String clientId, String reponseTime, String publicKey, String responseBody, String signatureToBeVerified) {
if (StringUtil.isBlank(signatureToBeVerified)) {
return false;
}
String content = String.format("POST %s\n%s.%s.%s", requestURI, clientId, reponseTime, responseBody);
try {
java.security.Signature signature = java.security.Signature
.getInstance("SHA256withRSA");
PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(
new X509EncodedKeySpec(Base64.decodeBase64(publicKey.getBytes("UTF-8"))));
signature.initVerify(pubKey);
signature.update(content.getBytes("UTF-8"));
return signature.verify(Base64.decodeBase64(URLDecoder.decode(signatureToBeVerified, "UTF-8").getBytes("UTF-8")));
} catch (Exception e) {
throw new RuntimeException(e);
}
}