Java程式碼實現摘要簽章認證方式

  • 文檔創建者:Roxy
  • 編輯次數:19
  • 最近更新:Nikozhan 于 2025-04-15
  • 1. 概述

    1.1 版本

    FineDataLink 版本
    功能說明
    4.0.29-

    1.2 應用場景

    對資料安全性要求高的使用者,需要對發佈的 API 進行更高安全性的鑑權方式設定,提供更安全的認證方式,滿足更嚴格的安全要求。

    1.3 功能說明

    資料服務發佈的 API 支援基於 AK/SK 認證邏輯的摘要簽章認證方式,避免認證資訊和請求資訊在傳輸程式中被截獲和篡改,提升認證安全性。

    FineDataLink 發佈的 API 採用 HMAC-SHA256 摘要算法計算簽章,驗籤邏輯為:

    請求發起方(計算簽章,並在請求中帶上簽章) ---> 請求接收方(按請求參數計算簽章,並校驗簽章)

    2. 前提條件

    使用者已經发布API,同時绑定API至应用,並設定應用認證方式為摘要認證。

    3. 簽章計算方法

    3.1 JAVA 簽章算法程式碼

    3.1.1 POST-application/json請求

    根據下列範例程式碼,建立JAVA 檔案:

    package kk;

    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import java.math.BigInteger;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.util.Base64;
    import java.util.UUID;

    public class k {
        public static void main(String[] args) {
            //修改為對應的AppSecret
            String secretKey = "1bbe91b1-a39c-4742-9694-e126bcf9a3bd";
            //請求方式,注意大寫
            String httpMethod = "POST";
            //POST請求需要寫contentType,GET請求此處寫空字串
            String contentType = "application/json";
            //API路徑。如果是get請求,需要帶上參數,例如xxx/xxx?a=1
            String pathAndParameters = "a5ce6bb4-467b-46f2-8878-2132635973bb/87";
            //body請求體的內容
            String data = "{\"paging\":{\"pageSize\":10,\"pageNum\":1},\"params\":[]}";

            //Nonce,自動生成
            String nonce = String.valueOf(UUID.randomUUID());
            //時間戳,自動生成
            String timestamp = String.valueOf(System.currentTimeMillis());
            //待簽章字串
            String stringToSign = httpMethod +"\n"+
                    nonce +"\n"+
                    timestamp +"\n"+
                    pathAndParameters +"\n"+
                    contentType +"\n"+
                    md5(data);
            //簽章
            String signature = hmacSHA256(secretKey,stringToSign);
            //拼出完整的Authorization
            System.out.println("Authorization:\n"+"HMAC-SHA256 Signature="+signature+",Nonce="+nonce+",Timestamp="+timestamp);
        }

        /**
         * 對字串data進行HmacSHA256簽章,以Base64的結果傳回
         *
         * @param secretKey 簽章金鑰
         * @param data      待簽章字串
         */
        public static String hmacSHA256(String secretKey, String data) {
            try {
                Mac hmacSha256 = Mac.getInstance("HmacSHA256");
                SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
                hmacSha256.init(secretKeySpec);

                byte[] hashBytes = hmacSha256.doFinal(data.getBytes(StandardCharsets.UTF_8));

                return Base64.getEncoder().encodeToString(hashBytes);
            } catch (Exception e) {
                throw new RuntimeException("HmacSha error", e);
            }
        }

        /**
         * 對字串data計算md5摘要值,再進行Base64編碼,傳回編碼後的結果
         *
         * @param data 待計算的字串
         */
        public static String md5(String data) {
            try {
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.reset();
                md.update(data.getBytes(StandardCharsets.UTF_8));
                String md5Result = String.format("%032x", new BigInteger(1, md.digest()));
                return Base64.getEncoder().encodeToString(md5Result.getBytes(StandardCharsets.UTF_8));
            } catch (Exception e) {
                throw new RuntimeException("md5 error", e);
            }
        }
    }

    3.1.2 GET請求

    package kk;

    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import java.math.BigInteger;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.util.Base64;
    import java.util.UUID;

    public class k {
        public static void main(String[] args) {
            //修改為對應的AppSecret
            String secretKey = "a07eefc1-4b29-469a-8cb1-f68e3532d3a2";
            //請求方式,注意大寫
            String httpMethod = "GET";
            //POST請求需要寫contentType,GET請求此處寫空字串
            String contentType = "";
            //API路徑。如果是get請求,需要帶上參數,例如xxx/xxx?a=1
            String pathAndParameters = "a5ce6bb4-467b-46f2-8878-2132635973bb/dd?pageSize=10&pageNum=1";
            //body請求體的內容
            String data = "";

            //Nonce,自動生成
            String nonce = String.valueOf(UUID.randomUUID());
            //時間戳,自動生成
            String timestamp = String.valueOf(System.currentTimeMillis());
            //待簽章字串
            String stringToSign = httpMethod +"\n"+
                    nonce +"\n"+
                    timestamp +"\n"+
                    pathAndParameters +"\n"+
                    contentType +"\n"+
                    ("".equals(data)?"":md5(data));
            //簽章
            String signature = hmacSHA256(secretKey,stringToSign);
            //拼出完整的Authorization
            System.out.println("Authorization:\n"+"HMAC-SHA256 Signature="+signature+",Nonce="+nonce+",Timestamp="+timestamp);
        }

        /**
         * 對字串data進行HmacSHA256簽章,以Base64的結果傳回
         *
         * @param secretKey 簽章金鑰
         * @param data      待簽章字串
         */
        public static String hmacSHA256(String secretKey, String data) {
            try {
                Mac hmacSha256 = Mac.getInstance("HmacSHA256");
                SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
                hmacSha256.init(secretKeySpec);

                byte[] hashBytes = hmacSha256.doFinal(data.getBytes(StandardCharsets.UTF_8));

                return Base64.getEncoder().encodeToString(hashBytes);
            } catch (Exception e) {
                throw new RuntimeException("HmacSha error", e);
            }
        }

        /**
         * 對字串data計算md5摘要值,再進行Base64編碼,傳回編碼後的結果
         *
         * @param data 待計算的字串
         */
        public static String md5(String data) {
            try {
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.reset();
                md.update(data.getBytes(StandardCharsets.UTF_8));
                String md5Result = String.format("%032x", new BigInteger(1, md.digest()));
                return Base64.getEncoder().encodeToString(md5Result.getBytes(StandardCharsets.UTF_8));
            } catch (Exception e) {
                throw new RuntimeException("md5 error", e);
            }
        }
    }

    3.1.3 POST-application/x-www-form-urlencoded請求

    package kk;

    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import java.math.BigInteger;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.util.Base64;
    import java.util.UUID;

    public class k {
        public static void main(String[] args) {
            //修改為對應的AppSecret
            String secretKey = "1bbe91b1-a39c-4742-9694-e126bcf9a3bd";
            //請求方式,注意大寫
            String httpMethod = "POST";
            //POST請求需要寫contentType,GET請求此處寫空字串
            String contentType = "application/x-www-form-urlencoded";
            //API路徑。如果是get請求,需要帶上參數,例如xxx/xxx?a=1
            String pathAndParameters = "a5ce6bb4-467b-46f2-8878-2132635973bb/87";
            //body請求體的內容  body裏的鍵值對也要編碼
            String data = "a=1&b=%E6%8C%AA%E5%A8%81";

            //Nonce,自動生成
            String nonce = String.valueOf(UUID.randomUUID());
            //時間戳,自動生成
            String timestamp = String.valueOf(System.currentTimeMillis());
            //待簽章字串
            String stringToSign = httpMethod +"\n"+
                    nonce +"\n"+
                    timestamp +"\n"+
                    pathAndParameters +"\n"+
                    contentType +"\n"+
                    md5(data);
            //簽章
            String signature = hmacSHA256(secretKey,stringToSign);
            //拼出完整的Authorization
            System.out.println("Authorization:\n"+"HMAC-SHA256 Signature="+signature+",Nonce="+nonce+",Timestamp="+timestamp);
        }

        /**
         * 對字串data進行HmacSHA256簽章,以Base64的結果傳回
         *
         * @param secretKey 簽章金鑰
         * @param data      待簽章字串
         */
        public static String hmacSHA256(String secretKey, String data) {
            try {
                Mac hmacSha256 = Mac.getInstance("HmacSHA256");
                SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
                hmacSha256.init(secretKeySpec);

                byte[] hashBytes = hmacSha256.doFinal(data.getBytes(StandardCharsets.UTF_8));

                return Base64.getEncoder().encodeToString(hashBytes);
            } catch (Exception e) {
                throw new RuntimeException("HmacSha error", e);
            }
        }

        /**
         * 對字串data計算md5摘要值,再進行Base64編碼,傳回編碼後的結果
         *
         * @param data 待計算的字串
         */
        public static String md5(String data) {
            try {
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.reset();
                md.update(data.getBytes(StandardCharsets.UTF_8));
                String md5Result = String.format("%032x", new BigInteger(1, md.digest()));
                return Base64.getEncoder().encodeToString(md5Result.getBytes(StandardCharsets.UTF_8));
            } catch (Exception e) {
                throw new RuntimeException("md5 error", e);
            }
        }
    }

    3.2 簽章算法原理

    3.2.1 用戶端生成並傳入參數

    用戶端需要傳入 Authorization 請求頭和 Content-Type 頭,使用 GET/POST 向服務端發起請求:

    請求頭
    內容
    Authorization

    算法名稱:Signature=Signature值, Nonce=Nonce隨機數, Timestamp=Timestamp時間戳

    舉例:

    HMAC-SHA256 Signature=FiXSVh+oBzitw+HDypwlmB0PE+Z67pdXbQM7QyHA7qk=, Nonce=c967a237-cd6c-470e-906f-a8655461897, Timestamp=1686542039670

    Content-Type

    根據請求的內容類型,填入Content-Type請求頭內容。

    如果是GET請求頭不需要傳入 Content-Type。

    其中生成 Signature 值作為「待簽章字串」需要的專案如下,其中標註字母的內容需要使用者根據自己的實際值進行取代:

    字母專案說明
    AAppSecret

    應用金鑰

    在資料服務>應用列表,選擇摘要簽章,即複製 AppSecret,如下圖所示:

    BHTTPMethod

    請求方法,目前支援傳入GET和POST。注:需要大寫。


    NonceUUID,在生成簽章的 Java 程式碼中自動生成。

    驗證時,服務端將會透過此 UUID 檢查近 5 分鐘內是否有重複請求,如果有重複請求,將進行攔截


    Timestamp

    13 位時間戳(1970-01-01 00:00:00開始到現在的毫秒數),在生成簽章的 Java 程式碼中自動生成

    驗證時,服務端將會透過此時間戳,檢查此請求是否已過期,如果請求已過期,將進行攔截

    DPathAndParameters

    API 配置頁面配置的 API 路徑

    如果是 get 請求,需要帶上參數,例如xxx/xxx?a=1

    範例:http://192.168.5.175:8089/webroot/service/publish/<appid>/api01?a=1&b=2中的粗體部分,不包含首尾的斜槓

    在 API 列表下的授權應用中複製 API 路徑,如下圖所示:

    CContent-Type

    BODY 的 MIME 類型

    POST 請求需要寫 contentType,GET 請求此處寫空字串

    EString data

    POST 請求時,在 BODY 中的參數字串

    • 對於json:原樣所有字串,包含空格、回車等符號

    • 對於x-www-form-urlencoded:將鍵值對分別進行 URL 轉碼,然後使用&連結起來;有鍵無值、有值無鍵時正常保留等號

    例如 JSON 格式的 Body,範例為:

    {"paging":{"pageSize":10,"pageNum":1},"params":[]}

    對於以上請求體內容,先做 MD5 處理(匯出32位小寫字串),結果再進行 BASE64 編碼,得到最終的 Content-MD5 結果。

    「待簽章字串」構建方式:


    待簽章字串 = 「HTTPMethod」+ "\n"

    +「Nonce」+ "\n"

    +「Timestamp」+ "\n"

    +「PathAndParameters」+ "\n"

    +「Content-Type」+ "\n"

    +「Content-MD5」

    注1:加號代表連接,實際是每個專案之間透過換行分隔,如果沒有對應專案,則對應內容用空字串代替,但是換行需要儲存

    注2:如果沒有 BODY,那麼 Content-MD5 專案直接為空字串,不對空 BODY 做 MD5(也就是說,目前對於 GET 請求,Content-Type 和 Content-MD5 專案都會為空字串)

    待簽章字串生成 Signature 簽章,使用 HMAC-SHA256 加密算法。

    Mac hmacSha256 = Mac.getInstance("HmacSHA256");
    byte[] appSecretBytes = appSecret.getBytes(Charset.forName("UTF-8"));
    hmacSha256.init(new SecretKeySpec(appSecretBytes, 0, appSecretBytes.length, "HmacSHA256"));
    byte[] md5Result = hmacSha256.doFinal(stringToSign.getBytes(Charset.forName("UTF-8")));
    String signature = Base64.encodeBase64String(md5Result);

    3.2.2 服務終止受請求

    計算簽章方式同用戶端

    校驗專案:

    驗證專案
    透過條件
    Timestamp與服務端當前時間相比,時間差在5min以內
    Nonce服務端在5min內,沒有重複的Nonce
    Signature服務端計算的 Signature 與請求頭中的 Signature 完全一致

    4. 簽章計算範例(以java為例)

    4.1 用戶端生成簽章

    參考本文 3.1 節修改程式碼中的指定內容:

    例如使用者已經在 FineDataLink 中發佈請求為 POST application/json 的 API ,則需要準備以下資料,以備生成待簽章字串:

    專案說明JAVA檔案中需要修改的內容
    AppSecret

    應用金鑰

    在資料服務>應用列表,選擇摘要簽章,即複製 AppSecret,如下圖所示:

    HTTPMethod

    請求方法,目前支援傳入 GET 和 POST注:需要大寫

    NonceUUID,在生成簽章的 Java 程式碼中自動生成

    驗證時,服務端將會透過此 UUID 檢查近 5 分鐘內是否有重複請求,如果有重複請求,將進行攔截


    Timestamp

    13 位時間戳(1970-01-01 00:00:00開始到現在的毫秒數),在生成簽章的 Java 程式碼中自動生成

    驗證時,服務端將會透過此時間戳,檢查此請求是否已過期,如果請求已過期,將進行攔截


    PathAndParameters

    API 配置頁面配置的 API 路徑

    如果是 get 請求,需要帶上參數,例如xxx/xxx?a=1

    範例:http://192.168.5.175:8089/webroot/service/publish/<appid>/api01?a=1&b=2中的粗體部分,不包含首尾的斜槓

    在 API 列表下的授權應用中複製 API 路徑,如下圖所示:

    Content-Type

    BODY 的 MIME 類型。

    POST 請求需要寫 contentType,GET 請求此處寫空字串


    Content-MD5

    POST請求時,在B ODY 中的參數字串。

    • 對於 json:原樣所有字串,包含空格、回車等符號

    • 對於 x-www-form-urlencoded:將鍵值對分別進行 URL 轉碼,然後使用&連結起來;有鍵無值、有值無鍵時正常保留等號

    例如 JSON 格式的 Body,範例為:

    {"paging":{"pageSize":10,"pageNum":1},"params":[]}

    對於 json 格式的 Body,可在 API 生成介面複製 Body 值

    對於以上請求體內容,先做 MD5 處理(匯出32位小寫字串),結果再進行 BASE64 編碼,得到最終的 Content-MD5 結果

    然後運作 JAVA 檔案,即可獲得 Authorization ,如下圖所示:

    4.2 服務端獲取請求

    此處以 POST JSON 請求為例,輸入 Authorization 認證和 Body 請求值,即可取出發佈的 API 資料,如下圖所示:




    附件列表


    主題: 資料服務
    已經是第一篇
    已經是最後一篇
    • 有幫助
    • 沒幫助
    • 只是瀏覽
    • 评价文档,奖励 1 ~ 100 随机 F 豆!