全部产品
Search
文档中心

SuperApp:安全机制

更新时间:Nov 07, 2024

互联网并不是一个十分安全的网络环境,针对一些关键API,如支付、借贷等API,为了防止数据泄漏与篡改,需要使用数据加密与签名的机制。

签名机制

对所发送的内容进行加签(签名),而接受者需要对内容进行验签(签名),证明内容没有被篡改,以此来保障开发者应用和开放平台交互的安全性。

一个完整的签名请求流程如下:

image

请求结构

Request Structure

image

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

image

Response Body

Response Body包含返回给客户端的业务信息,同时也包含请求的结果信息resultCode、resultMessage。

Field

Data Type

Required

Description

resultCode

String

No

结果代码.

最大长度:64个字符。

resultMessage

String

No

结果代码的详细描述。

最大长度:256个字符。

加签/验签

1. 加签请求

  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****"
      }
  2. 基于以上信息,{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
    
  3. 使用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%2BKdwbw4eY7tZBDQhMKoaMVSbqbCb3eRBxxxx
  • response 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);
    }

}