开放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"
})