You need to enable JavaScript to run this app.
导航
调用方式
最近更新时间:2024.12.05 16:07:02首次发布时间:2024.04.30 15:17:27

增长分析(即DataFinder) 所有开放接口,均建议使用 https 进行通信。所有接口都需要经过统一的签名处理。为了方便集成和使用,DataFinder提供了 SDK,本文为您介绍DataFinder开发接口的签名机制和使用说明。

服务地址

OpenAPI 的服务地址在中国区和非中国区是隔离不互通的,OpenAPI 的服务地址需要根据所在地区进行设置。

  • 中国区:https://analytics.volcengineapi.com
  • 非中国区: https://analytics.byteplusapi.com
  • 私有化部署:根据私有化部署的环境来获取,即产品的域名地址。

签名机制

OpenAPI 使用 AK/SK 认证鉴权。

1. 获取AK/SK

说明

AK是平台为每个集团默认创建的;SK全局唯一,自行输入(长度 6~64个字符),请获取后妥善保管。

  • SaaS环境:您需要提交工单联系火山引擎技术申请获取AK/SK,申请通过后,AK/SK会发送到您的火山账号绑定的安全邮箱中,您可以前往邮箱查看,收到AK/SK后请务必妥善保管和使用。
  • 私有化环境:集团管理可在右上角“集团项目概览”(如图所示)中,创建或者重置AK/SK。
    Image

2. 签名说明

AK/SK认证就是使用AK/SK对请求进行签名,在请求时将签名信息添加到消息头,从而通过身份认证。

  • AK(Access Key ID):访问密钥ID。与私有访问密钥关联的唯一标识符;访问密钥ID和私有访问密钥一起使用,对请求进行加密签名。
  • SK(Secret Access Key):与访问密钥ID结合使用的密钥,对请求进行加密签名,可标识发送方,并防止请求被修改。

请求发送方使用方式如下:
Image

  1. 以 “ak-v1/access_key/timestamp/expiretime"为认证字符串前缀。

    • ak-v1为版本号,用来表示一个认证字符串。
    • timestamp为代表签名生效UTC时间。
    • expiretime为签名有效期限。
  2. 使用签名算法(HmacSHA256),以认证字符串前缀为消息,secret_key为密钥,生成字符串sign_key。

  3. 把需要加密的字段放入canonicalRequest,其格式为:

    CanonicalRequest =
        HTTPMethod:${method} + '\n' +
        CanonicalURI:${uri} + '\n' +
        CanonicalQueryString:${queryString} + '\n' +
        CanonicalBody:${body}
    

    使用签名算法(HmacSHA256),以canonicalRequest为消息,sign_key为密钥,生成字符串signature。

  4. 最终的认证字符串为 “ak-v1/access_key/timestamp/expiretime/signature”。

  5. 把认证字符串放在http的header:Authorization:ak-v1/access_key/timestamp/expiretime/signature

使用说明

1.概述

为了方便客户调用 OpenAPI, DataFinder提供了 OpenAPI SDK, 其主要功能是提供了对签名过程和复杂查询参数的包装。SDK 已经在 Github 上开源,建议使用Github 源码的方式。基本使用流程:

  1. 根据ak, sk, API 服务地址初始化一个RangersClient (后文会使用bc来指一个RangersClient实例)
  2. 使用RangersClient的request接口来调用 API
ak = '{使用AK替换}'
sk = '{使用SK替换}'

# saas 
bc = RangersClient(ak, sk)

# 海外使用非中国区的地址;私有化,需要制定下私有化的finder服务域名,请参考 “OpenAPI 概览”的服务地址说明。
url = '{使用非中国区或者Finder服务域名替换}'
bc = RangersClient(ak, sk, url=url)

# 调用具体的接口
bc.request("${path}",...)

相关接口说明(具体的方法名称在不同的语言上会有命名格式的区别)

  • bc.data_finder("${path}", ...), 相当于 bc.request("/datafinder/${path}", ...),即在path前,加上一个/datafinder的context-path
  • bc.data_rangers("${path}", ...), 相当于bc.request(/datarangers/${path}", ...),即在path前,加上一个/datarangers的context-path
  • bc.data_tester("${path}", ...), 相当于 bc.request("/datatester/${path}", ...),即在path前,加上一个/datatester的context-path
  • bc.data_profile("${path}", ...), 相当于 bc.request("/dataprofile/${path}", ...),即在path前,加上一个/dataprofile的context-path
  • bc.data_tracer("${path}", ...), 相当于 bc.request("/datatracer/${path}", ...),即在path前,加上一个/datatracer的context-path

建议后续都使用bc.request接口
另外,OpenAPI 还提供了查询 DSL Builder,具体接口参考源码 API 说明。

2.Java

  • 源码:https://github.com/volcengine/datafinder-sdk-openapi-java
  • 软件包:https://github.com/volcengine/datafinder-sdk-openapi-java/raw/main/release/sdk-openapi-java.zip
  • 初始化示例:
String ak = "xxx";
String sk = "xxx";

// SDK 的默认url地址是指向中国区 SAAS 的
RangersClient bc = new RangersClient(ak, sk);

// 海外和私有化需要指定url地址, 可以参考 “OpenAPI 概览”
String url="{使用非中国区或者Finder服务域名替换}";
RangersClient bc = new RangersClient(ak, sk, url);

3.Python

  • 源码:https://github.com/volcengine/datafinder-sdk-openapi-py
  • 软件包:https://github.com/volcengine/datafinder-sdk-openapi-py/raw/main/release/rangersdk-1.2.0.tar.gz

Python SDK 软件包的形式下载后在shell执行以下命令完成安装:

# python需要3.7及以上版本
pip install rangersdk-${version}.tar.gz
  • 初始化示例:
from rangersdk import RangersClient

ak = 'xxx' # ak
sk = 'xxx' # sk
bc = RangersClient(ak, sk)

# 海外和私有化需要指定url地址, 可以参考 “OpenAPI 概览”,
url = '{使用非中国区或者Finder服务域名替换}'
# 注意这里传参数,一定要写成 url=url
bc = RangersClient(ak, sk, url=url)

4.Js

  • 源码:https://github.com/volcengine/datafinder-sdk-openapi-js
  • 软件包:https://github.com/volcengine/datafinder-sdk-openapi-js/raw/main/release/nodejssdk.zip
  • 初始化示例:
ak = "xxx"
sk = "xxx"
bc = new RangersClient(ak, sk)

// 海外和私有化需要指定url地址, 可以参考 “OpenAPI 概览”
url = '{使用非中国区或者Finder服务域名替换}'
bc = RangersClient(ak, sk, url=url)

5.Golang

  • 源码:https://github.com/volcengine/datafinder-sdk-openapi-go
  • 软件包:https://github.com/volcengine/datafinder-sdk-openapi-go/raw/main/release/gosdk.zip
  • 初始化示例:
var (
   ak = "xxx"
   sk = "xxx"
)
bc := dslcontent.NewRangersClient(ak, sk)

// 海外和私有化需要指定url地址, 可以参考 “OpenAPI 概览”
url = '{使用非中国区或者Finder服务域名替换}'
bc := dslcontent.NewRangersClientWithUrl(ak, sk, url)

6.PHP

  • 源码:https://github.com/volcengine/datafinder-sdk-openapi-php
  • 软件包:https://github.com/volcengine/datafinder-sdk-openapi-php/raw/main/release/phpsdk.zip
  • 初始化示例:
ak = "xxx";
sk = "xxx";
$bc = new RangersClient($ak, $sk);

// 海外和私有化需要指定url地址, 可以参考 “OpenAPI 概览”
$url = '{使用非中国区或者Finder服务域名替换}'
$bc = new RangersClient($ak, $sk, $url);

示例代码

这里以Java 为例,其他的语言的代码可以查看 OpenAPI SDK 里面提供的源码。

签名 (AuthUtil.java)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;

public class AuthUtils {

        /**
     * 
     * @param ak accessKey
     * @param sk secretKey
     * @param expirationSeconds 过期时间,单位秒
     * @param method 方法,GET, POST, PUT
     * @param uriPath 请求的path,非完整的url
     * @param params 请求参数
     * @param body 请求的json体
     * @return
     */
    public static String sign(String ak, String sk, int expirationSeconds, String method, String uriPath, Map<String, String> params, String body) {
        String cm = canonicalMethod(method);
        String cu = canonicalUrl(uriPath);
        String cp = canonicalParam(params);
        String cb = canonicalBody(body);
        String text = cm + "\n" + cu + "\n" + cp + "\n" + cb;
        return doSign(ak, sk, expirationSeconds, text);
    }

    private static String canonicalMethod(String method) {
        return "HTTPMethod:" + method;
    }

    private static String canonicalUrl(String url) {
        return "CanonicalURI:" + url;
    }

    private static String canonicalParam(Map<String, String> params) {
        String res = "CanonicalQueryString:";
        if (params == null || params.isEmpty()) {
            return res;
        }
        for (String key : params.keySet()) {
            res += formatKeyValue(key, params.get(key)) + "&";
        }
        return res.substring(0, res.length() - 1);
    }

    private static String formatKeyValue(String key, String value) {
        return key + "=" + value;
    }

    private static String canonicalBody(String body) {
        String res = "CanonicalBody:";
        if (body == null) {
            return res;
        } else {
            return res + body;
        }
    }

    private static String doSign(String ak, String sk, int expiration, String text) {
        String signKeyInfo = "ak-v1/" + ak + "/" + (int) (System.currentTimeMillis() / 1000) + "/" + expiration;
        String signKey = sha256Hmac(signKeyInfo, sk);
        String signResult = sha256Hmac(text, signKey);
        return signKeyInfo + "/" + signResult;
    }


    private static String sha256Hmac(String message, String secret) {
        String hash = "";
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
            hash = byteArrayToHexString(bytes);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        return hash;
    }

    private static String byteArrayToHexString(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b != null && n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1) {
                hs.append('0');
            }
            hs.append(stmp);
        }
        return hs.toString().toLowerCase();
    }
}

调用

import java.util.LinkedHashMap;
import java.util.Map;

public class AuthGenDemo {

    //分配的accessKey和secretKey
    private static String accessKey = "****";
    private static String secretKey = "****";
    // 单位秒
    private static Integer expirationSeconds = 300;

    public static void main(String[] args) {
        String method = "POST";
        // 请求的path,非完整的url
        String uri = "/dataprofile/openapi/v1/751/users/185";
        Map<String, String> exampleQueryParams = new LinkedHashMap<>();
        exampleQueryParams.put("set_once", "true");
        String exampleQueryBodyJson = "{\"name\":\"name\",\"value\":\"zhangsan\"}";

        String authorization = AuthUtils.sign(accessKey, secretKey, expirationSeconds,
                method, uri, exampleQueryParams, exampleQueryBodyJson);
        System.out.println("authorization: " + authorization);
    }
}

FAQ

Q1:调用OpenAPI时报错,'code': 400, 'message': 'signature expired'

此类报错错误通常是由于签名过期导致的。这种情况可能有以下几种原因:

可能原因

解决方案

时间不同步。
服务器和客户端的时间不同步,导致签名生成的时间戳与服务器验证的时间戳不一致。

确保两台机器的系统时间是同步的,可以使用NTP(网络时间协议)进行时间同步。

签名有效期过短。
签名的有效期设置得太短,导致在请求到达服务器时签名已经过期。

检查签名生成时设置的有效期参数,确保其足够长。

请求延迟。
网络延迟或其他原因导致请求在传输过程中耗时过长,超过了签名的有效期。

可以尝试增加签名的有效期。

签名生成错误。
签名生成过程中可能存在错误,导致生成的签名不正确。

检查签名生成的代码逻辑,确保其正确无误。