反馈已提交

网络繁忙

Java代码实现摘要签名认证方式

  • 文档创建者:Roxy
  • 历史版本:18
  • 最近更新:Roxy 于 2024-10-12
  • 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 豆!

    鼠标选中内容,快速反馈问题

    鼠标选中存在疑惑的内容,即可快速反馈问题,我们将会跟进处理。

    不再提示

    10s后关闭



    AI

    联系我们
    在线支持
    获取专业技术支持,快速帮助您解决问题
    工作日9:00-12:00,13:30-17:30在线
    页面反馈
    针对当前网页的建议、问题反馈
    售前咨询
    采购需求/获取报价/预约演示
    或拨打: 400-811-8890 转1
    qr
    热线电话
    咨询/故障救援热线:400-811-8890转2
    总裁办24H投诉:17312781526
    提交页面反馈
    仅适用于当前网页的意见收集,帆软产品问题请在 问答板块提问前往服务平台 获取技术支持