开放API对接文档

1. 概述


  • 接口采用 HTTP 协议,编码格式为 UTF-8,响应数据类型为 application/json,响应数据为 JSON 格式数据;
  • 通过返回的 HTTP 状态码判断调用是否成功,2xx 代表成功;
  • 如果错误,返回统一的错误信息,格式如下(具体的错误码见错误码定义):


{
  "code": 401,
  "message": "权限不足"
}


2. AK/SK签名认证算法详解

AK(AccessKey ID)/ SK(Secret Access Key)

获取地址:应用列表 -> 应用详情 -> 开发管理 -> 应用密钥

2.1 客户端涉及的 AK/SK 签名以及请求发送的流程概述如下:


  • 构造请求体,将待发送的请求内容按照与 API 网关后台约定的规则组装,确保客户端签名、API 网关后台认证时使用的请求内容一致。
  • 使用规范请求和其他信息创建待签字符串。
  • 使用 AK/SK 和待签字符串计算签名。
  • 将生成的签名信息作为请求消息头添加到 HTTP 请求中


2.2 详细说明


使用AK/SK方式进行签名与认证,首先需要规范请求内容,然后再进行签名。客户端与 API 网关使用相同的请求规范,可以确保同一个 HTTP 请求的前后端得到相同的签名结果,从而完成身份校验。


2.3 签名生成规则:


2.3.1 签名体分为5个部分:


  • HTTPRequestMethod: 表示请求的http方法,大写, 如POST、GET、PUT
  • URI: 表示请求的路径,以 ”/“ 开头,并以 ”/“ 结尾,当结尾不是”/“ 需要补上”/“,如:/api/user 需要变为 /api/user/
  • QueryString: 表示请求的url参数,参数名按 ASCII 码从小到大排序(字典序,不需要 urlEncode),如果参数的值为空也要保留,如a=&c=10
  • Timestamp: 表示请求的时间戳,单位秒,从 header 头 X-Timestamp 获取
  • RequestPayload:表示请求的body,只有当header: Content-Type = "application/json"  并且body不为空时才需要将 RequestPayload 加入加密体,否则最后一个 "@" 也要省略


2.3.2 签名生成规则伪代码如下:


CanonicalRequest = HTTPRequestMethod + '@' + CanonicalURI + '@' +CanonicalQueryString + '@' +Timestamp + '@' + RequestPayload


2.3.3 签名注意事项:


  • 时间戳,AK,签名值 分别使用 header: X-Timestamp、X-AccessKey、X-Signature
  • X-Timestamp 时间戳精确到秒(10位)
  • 各个数据块之间用 @ 隔开
  • X-AccessKey 不传时无法通过校验



3.官方demo示例


官方demo原始请求


POST https://open-api.gaoding.com/api/auth-demo
URI: /api/auth-demo/
请求body: {"str":"demo-test"}


加密


加密体 POST@/api/auth-demo/@@1637291905@{"str":"demo-test"}


加密方式使用 SK和 CanonicalRequest 进行 Hmac sha1 计算得到签名值 ,然后再做 encode_base64,得出最终 X-Signature,放入header头

加密结果:i+tnN5wZt2dlPUXxzs0acBQZJ1s=


最终请求体为:


POST https://open-api.gaoding.com/api/auth-demo
Header: Content-Type: application/json
Header: X-Timestamp = 1637291905
Header: X-AccessKey = ********************
Header: X-Signature = i+tnN5wZt2dlPUXxzs0acBQZJ1s=


响应结果:


{
  "message":"调用稿定开放平台DemoAPI成功,请求body:{str=调用开放平台官方demo}"
}


4. 一键复制接入代码



5. 请求示例


Java示例:


import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
 
 
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
 
public class HashHmacTest {
    public static String getHMAC(String data, String key) throws Exception {
        String HMAC_SHA1_ALGORITHM = "HmacSHA1";
        SecretKeySpec signinKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM);
        Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
        mac.init(signinKey);
        byte[] rawHmac = mac.doFinal(data.getBytes());
        return new String(Base64.encodeBase64(rawHmac));
    }
 
 
    public static void main(String[] args) throws Exception {
        // TODO 使用真实AK
        String ak = "********************************";
        // TODO 使用真实SK
        String sk = "********************************";
        // TODO 使用真实接口method
        String httpMethod = "POST";
        // TODO 使用真实接口uri,记住前后必须加‘/’
        String uri = "/api/call/mattingportrait/";
        String queryString = "";
        JSONObject jsonBody = new JSONObject();
        // TODO 使用真实接口入参
        jsonBody.put("url", "https://st-gdx.dancf.com/gaodingx/10108011/clip/20191022-155924-2d56.jpg");
        Long timestamp = System.currentTimeMillis() / 1000;
 
 
        String requestRaw = StringUtils.join(new String[]{httpMethod, "@", uri, "@", queryString, "@", timestamp.toString(), "@", jsonBody.toJSONString()}, "");
        String signature = getHMAC(requestRaw, sk);
        CloseableHttpClient client = HttpClientBuilder.create().build();
        // TODO 使用真实接口地址
        HttpPost httpPost = new HttpPost("https://open-api.gaoding.com/api/call/mattingportrait");
        CloseableHttpResponse response = null;
 
 
        try {
            httpPost.addHeader("Content-Type", "application/json");
            httpPost.addHeader("X-Timestamp", timestamp.toString());
            httpPost.addHeader("X-AccessKey", ak);
            httpPost.addHeader("X-Signature", signature);
            httpPost.setEntity(new StringEntity(jsonBody.toString()));
            response = client.execute(httpPost);
            HttpEntity responseEntity = response.getEntity();
            System.out.println("响应状态为:" + response.getStatusLine());
            if (responseEntity != null) {
                System.out.println("响应内容为:" + EntityUtils.toString(responseEntity));
            }
        } finally {
            try {
                // 释放资源
                if (client != null) {
                    client.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
 
}


PHP示例:


<?php
 
// TODO 使用真实AK
$ak = "********************************";
// TODO 使用真实SK
$sk = "********************************";
// TODO 使用真实接口method
$http_method="POST";
// TODO 使用真实接口uri,注意前后必须加‘/’
$uri = "/api/call/mattingportrait/";
$query_string = "";
$time_stamp = time();
// TODO 使用真实接口入参
$dict_body=["url"=>"https://st-gdx.dancf.com/gaodingx/10108011/clip/20191022-155924-2d56.jpg"];
$dict_str=json_encode($dict_body,true);
$request_raw = $http_method."@".$uri."@".$query_string."@".$time_stamp."@".$dict_str;
var_dump($request_raw);
 
$signature = hash_hmac("sha1",$request_raw, $sk,true);
$signature = base64_encode($signature);
var_dump($signature);
$headers=[
    "Content-Type:application/json",
    "X-Timestamp:".$time_stamp,
    "X-AccessKey:".$ak,
    "X-Signature:".$signature
];
$curl = curl_init();
// TODO 使用真实接口地址
$resquest_api = "https://open-api.gaoding.com/api/call/mattingportrait";
curl_setopt($curl, CURLOPT_URL, $resquest_api);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST,FALSE);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $dict_str);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_NOBODY, FALSE);
 
$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
var_dump($status);
var_dump($response);
 
curl_close($curl);


Python示例:


import json
import base64
import hmac
from hashlib import sha1
import requests
import time
 
 
 
def hash_hmac(data, key, sha1):
    hmac_code = hmac.new(key.encode(), data.encode(), sha1).digest()
    return base64.b64encode(hmac_code).decode()
 
 
if __name__ == '__main__':
    print("--------demo---------")
 
    // TODO 使用真实AK
    ak = "********************************"
    // TODO 使用真实SK
    sk = "********************************"
    // TODO 使用真实接口method
    http_method = "POST"
    // TODO 使用真实uri,注意前后必须加‘/’
    uri = "/api/call/mattingportrait/"
    query_string = ""
    time_stamp = str(int(time.time()))
    print(time_stamp)
 
    dict_body = {}
    // TODO 使用真实接口入参
    dict_body["url"] = "https://st-gdx.dancf.com/gaodingx/10108011/clip/20191022-155924-2d56.jpg"
    json_body = json.dumps(dict_body)
 
    list_raw = [http_method, "@", uri, "@", query_string, "@", time_stamp, "@", json_body]
    request_raw = "".join(list_raw)
    print(request_raw)
 
    signature = hash_hmac(request_raw, sk, sha1)
    print(signature)
 
    // TODO 使用真实接口地址
    resquest_api = "https://open-api.gaoding.com/api/call/mattingportrait"
    headers = {'Content-Type': 'application/json', 'X-Timestamp':time_stamp, 'X-AccessKey':ak, "X-Signature":signature}
    resp = requests.post(resquest_api, headers=headers, data=json_body)
    code = resp.status_code
    print(code)
    resp_data = resp.text
    print(resp_data)


C++示例:


#include <iostream>
#include <curl/curl.h>
#include<json/json.h>
#include <time.h>
#include <string.h>
#include "hmac/hmac_hash.h"
#include "base64/base64.h"
 
using namespace std;
// libcurl库下载链接:https://curl.haxx.se/download.html
// jsoncpp库下载链接:https://github.com/open-source-parsers/jsoncpp/
//base64 库下载链接 https://github.com/ReneNyffenegger/cpp-base64
//hmac 下载地址 https://blog.csdn.net/yasi_xi/article/details/9066003
 
const static std::string strRequestUrl = "https://open-api.gaoding.com/api/call/mattingportrait";
static std::string segPersonResult;
 
/**
 * curl发送http请求调用的回调函数,回调函数中对返回的json格式的body进行了解析,解析结果储存在全局的静态变量当中
 * @param 参数定义见libcurl文档
 * @return 返回值定义见libcurl文档
 */
static size_t callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
    // 获取到的body存放在ptr中,先将其转换为string格式
    segPersonResult = std::string((char *) ptr, size * nmemb);
    return size * nmemb;
}
 
/**
 * 人像抠图
 * @return 调用成功返回0,发生错误返回其他错误码
 */
int segPerson(std::string &strJsonResult,std::string &strJsonBody,std::string &strAccessKey,std::string &strTimeStamp,std::string & strEncoded)
{
    std::string url = strRequestUrl;
    CURL *pCurl = NULL;
    struct curl_slist* pHeaders = NULL; 
    CURLcode nResultCode;
    int nSuccess;
    pCurl = curl_easy_init();
    if (pCurl)
    {
        //设置请求头
        string strHeadTimeStap = "X-Timestamp: "+strTimeStamp;
        string strHeadAk = "X-AccessKey: "+strAccessKey;
        string strHeadSn = "X-Signature: "+strEncoded;
 
        pHeaders = curl_slist_append(pHeaders,"content-type:application/json");
        pHeaders = curl_slist_append(pHeaders, strHeadTimeStap.c_str());
        pHeaders = curl_slist_append(pHeaders, strHeadAk.c_str());
        pHeaders = curl_slist_append(pHeaders, strHeadSn.c_str());
        curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, pHeaders);
        curl_easy_setopt(pCurl, CURLOPT_URL, url.data());
        curl_easy_setopt(pCurl, CURLOPT_POST, 1);
        curl_easy_setopt(pCurl, CURLOPT_POSTFIELDS, strJsonBody.c_str());
        curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, callback);
        nResultCode = curl_easy_perform(pCurl);
        if (nResultCode != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(nResultCode));
            nSuccess = 1;
            return nSuccess;
        }
        strJsonResult = segPersonResult;
        curl_easy_cleanup(pCurl);
        nSuccess = 0;
    }
    else
    {
        fprintf(stderr, "curl_easy_init() failed.");
        nSuccess = 1;
    }
    return nSuccess;
}
 
int main()
{
    Json::Reader reader;
    Json::Value reqData;
    // TODO 使用真实AK
    string strAccessKey = "********************************";
    // TODO 使用真实SK
    string strSecretkey = "********************************";
    // TODO 使用真实接口method
    string strHttpMethod = "POST";
    // TODO 使用真实接口uri,注意前后必须加‘/’
    string strUri = "/api/call/mattingportrait/";
    string strQueryString = "";
    // 选择加密方式为sha1
    string strAlg = "sha1";
    time_t lt = time(NULL);
    string strTimeStamp = std::to_string(int(lt));
 
    // TODO 使用真实接口入参
    reqData["url"] = Json::Value("https://st-gdx.dancf.com/gaodingx/10108011/clip/20191022-155924-2d56.jpg");
    std::string strJsonBody = reqData.toStyledString();
 
    string strRequestRaw = strHttpMethod + "@" + strUri + "@" + strQueryString + "@" + strTimeStamp +"@" +strJsonBody ;
 
    unsigned char * pMac = NULL;
    unsigned int nMacLength= 0;
    //hsmc - sha1加密
    int ret = HmacEncode(strAlg.c_str(), strSecretkey.c_str(), strSecretkey.length(), strRequestRaw.c_str(), strRequestRaw.length(), pMac, nMacLength);
    // base64 编码
    std::string strEncoded = base64_encode(reinterpret_cast<const unsigned char*>(pMac), nMacLength);
    //取出结果
    std::string strResult = "";
    int nCode = segPerson(strResult,strJsonBody,strAccessKey,strTimeStamp,strEncoded);
    cout<<strResult<<endl;
    return 0;
}


Nodejs示例:

const axios = require('axios');
const {
    createHmac
} = require('crypto');

axios.defaults.baseURL = 'https://open-api.gaoding.com';
axios.interceptors.request.use(async (config) => {
    const timestamp = Math.floor(Date.now() / 1000);
    //使用真实的AK
    const ak = '********************************';
    //使用真实的SK
    const sk = '********************************';
    config.headers['x-AccessKey'] = ak;
    config.headers['x-Timestamp'] = timestamp;
    let canonicalRequest = `${config.method.toUpperCase()}@${(config.url + '/').replace(
        /\/\/$/,
        '/',
    )}`;
    if (config.params) {
        const qs = Object.keys(config.params || {})
            .sort()
            .map((k) => `${k}=${config.params[k]}`)
            .join('&');
        canonicalRequest += `@${qs}`;
    } else {
        canonicalRequest += '@';
    }
    canonicalRequest += `@${timestamp}`;
    if (config.data) {
        canonicalRequest += `@${JSON.stringify(config.data)}`;
    }
    console.log(canonicalRequest);
    config.headers['x-Signature'] = createHmac('sha1', sk)
        .update(canonicalRequest)
        .digest('base64');
    return config;
});

axios.interceptors.response.use(function (response) {
    console.log(response.data)
    return response;
}, function (error) {
    return Promise.reject(error);
});
//使用真实的接口地址
axios.post('/api/call/mattinggraphs/',{
    url: "https://st-gdx.dancf.com/matting/passport-photo/20200713-114040-7e97.jpg"
})