请求签名鉴权

在调用API 前,需要拼接签名字符串,并将签名计算后的字符串放在请求的 Header 传入,网关会通过的Request 的 Header 中的 X-Tsign-Open-Ca-Signature获取签名,然后通过计算签名来验证请求者的身份。

请求签名的Header包含以下字段:

  • 【必选】X-Tsign-App-Id:应用的 AppId。
  • 【必选】X-Tsign-Open-Auth-Mode:告诉网关当前哪种方式认证,请求签名接入时传Signature
  • 【必选】X-Tsign-Open-Ca-Signature:签名字符串
  • 【必选】X-Tsign-Open-Ca-Timestamp:API 调用者传递时间戳,值为当前时间的毫秒数,也就是从1970年1月1日起至今的时间转换为毫秒,时间戳有效时间为15分钟,为了防重放攻击。
  • 【可选】Content-MD5: 当请求 Body 非 Form 表单时,可以计算 Body 的 MD5 值传递给云网关进行 Body MD5 校验。建议当请求 Body 非 Form 表单时,加上此请求头。
  • 【可选】X-Tsign-open-Ca-Signature-Headers:当需要对headers进行签名时需要加上此请求头。

其中重点需要计算的是X-Tsign-Open-Ca-Signature (签名字符串),签名字符串的生成分两个步骤:拼接字符串和计算签名。

1.签名字符串生成

步骤一:待签字符串准备

待签字符串由以下字段拼接组成

参数 说明
HTTPMethod 全大写,例如POST
Accept 希望服务器响应发送回来的是xml文本格式的内容,例如:application/json
Content-MD5 Content-MD5 是指 Body 的 MD5 值,只有当 Body 非 Form 表单时才计算 MD5
Content-Type 请求的与实体对应的MIME信息,例如:application/json
Date 可选,Date头域表示消息发送的时间,缓存在评估响应的新鲜度时要用到,时间的描述格式由RFC822定义。例如,Date: Thu, 11 Jul 2015 15:33:24 GMT。
Headers 可选,对请求的自定义Header进行签名,生成方法见下方
Url Url ,对请求的url进行签名,生成方法见下方

(1)待签字符串的拼接方法

String stringToSign= 
HTTPMethod + "\n" + 
Accept + "\n" +
Content-MD5 + "\n" 
Content-Type + "\n" + 
Date + "\n" + 
Headers + 
Url

其中HTTPMethod为全大写,Accept、Content-MD5、Content-Type、Date 如果为空也需要添加换行符”\n”,Headers如果为空不需要添加”\n”。

(2)Content-MD5的计算方法

Content-MD5 是指 Body 的 MD5 值,只有当 Body 非 Form 表单时才计算 MD5,计算方式为:

String content-MD5 = Base64.encodeBase64(MD5(bodyStream.getbytes("UTF-8")));

其中bodyStream 为字节数组。

(3)Headers的添加方法

Headers 是指参与 Headers 签名计算的 Header 的 Key、Value 拼接的字符串,建议对 X-Tsign 开头以及自定义 Header 计算签名。若无需对Headers进行签名,可以设为空。

先对参与 Headers 签名计算的 Header的Key 按照字典排序后使用如下方式拼接,如果某个 Header 的 Value 为空,则使用 HeaderKey + “:” + “\n”参与签名,需要保留 Key 和英文冒号。

String headers =
HeaderKey1 + ":" + HeaderValue1 + "\n"\+
HeaderKey2 + ":" + HeaderValue2 + "\n"\+
...
HeaderKeyN + ":" + HeaderValueN + "\n"

若添加了Headers,则需要将 Headers 签名中 Header 的 Key 使用英文逗号分割放到 Request 的 Header 中,Key为:X-Tsign-open-Ca-Signature-Headers。

(4)Url的添加方法

Url 指 Path + Query + BodyForm 参数,组织方法:对 Query+Form 参数按照字典对 Key 进行排序后按照如下方法拼接,如果 QueryForm 参数为空,则 Url = Path,不需要添加 ,如果某个参数的 Value 为空只保留 Key 参与签名,等号不需要再加入签名。

String url =
Path +
"?" +
Key1 + "=" + Value1 +
"&" + Key2 + "=" + Value2 +
...
"&" + KeyN + "=" + ValueN

注意这里 QueryForm 参数的 Value 可能有多个,多个的时候只取第一个 Value 参与签名计算。

步骤二:计算签名字符串

对待签字符串使用HmacSHA256进行签名运算,从而得到签名字符串,计算方法如下:

Mac hmacSha256 = Mac.getInstance("HmacSHA256");
byte[] keyBytes = secret.getBytes("UTF-8");
hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, "HmacSHA256"));
String sign = new String(Base64.encodeBase64(hmacSha256.doFinal(stringToSign.getBytes("UTF-8")),"UTF-8"));

2.签名字符串生成代码示例

import Utils.CommonApi;
import com.alibaba.fastjson.JSONObject;
import constants.Constants;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

public class Demo {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
        String url = "/v1/accounts/elogin/sign";
        String encryptContent = "N9SxYsmDaYnztLOHio9n9R13yeF9GaZOUE6c4GkRL7ifyQarDk9jeVypke9iWZwQCa/SP5ewC+jb6ecAJYsAIXskfXGBiQG/+j1qukHsc5/BF8etGF7Mzgwdl2gBo6AJmIavDSnfmNip2cn6TCwnxqddfHklYWNteOrjEDCXm/ieXhR37BYyYLBGSjITkTXE9+cdXbOlBT+WozjP4TKL8Fo/yCe7XNppV/kCseRkU9k8j97QkLYENhg6CLTv1fM8BDyfBaQRFWituIxaq+xX3KpvKRl7mQ/QX5gdOVFuv1NsysJbyA8FOpF/0bPA3UGfjparjjvOItc+YeLlHMFKYg==";
        String shortLinkUrl = "https://smlt.tsign.cn/aIa71lcWtzkF";
        String appid = "4438779132";
        String secret = "ce0c19c6728c52dfc417beb405c8824d";

        String enter = "\n";
        String method = "POST";
        String accept = "application/json";
        String contentType = "application/json; charset=UTF-8";
        String contentMD5 = "";
        String date = "";
        String headers = "";
        StringBuilder sb = new StringBuilder();
        sb.append(method).append(enter).append(accept).append(enter)
                .append(contentMD5).append(enter).append(contentType).append(enter)
                .append(date).append(enter);
        if ("".equals(headers)){
            sb.append(headers).append(url);
        } else {
            sb.append(headers).append(enter).append(url);
        }

        String stringToSign = sb.toString();
        System.out.println(stringToSign);

        Mac hmacSha256 = Mac.getInstance("HmacSHA256");
        byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
        hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, "HmacSHA256"));
        String sign = new String(Base64.encodeBase64(hmacSha256.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8);

        JSONObject params = new JSONObject();
        params.put("encryptContent", encryptContent);
        params.put("shortLinkUrl", shortLinkUrl);

        Map<String, String> header = new HashMap<>();
        header.put("Content-Type", contentType);
        header.put("X-Tsign-Open-App-Id", appid);
        header.put("X-Tsign-Open-Auth-Mode", "Signature");
        header.put("X-Tsign-Open-Ca-Signature", sign);
        header.put("X-Tsign-Open-Ca-Timestamp", String.valueOf(System.currentTimeMillis()));
        header.put("Accept", accept);

        CommonApi.requestJson(url, Constants.HOST_ELOGIN, params, header);

    }
}

results matching ""

    No results matching ""