百度 AIP 开放平台使用 OAuth2.0 授权调用开放 API,调用 API 时必须在 URL 中带上 access_token 参数。
请求 URL 数据格式
请求参数如下:
grant_type: 必须参数,固定为 client_credentials;
client_id: 必须参数,应用的 API Key;
client_secret: 必须参数,应用的 Secret Key;
获取结果
服务器返回的JSON文本参数如下:
access_token: 要获取的 Access Token;
expires_in: Access Token 的有效期(秒为单位,一般为 1 个月);
其他参数忽略,暂时不用;
以下代码为示例:
{
"refresh_token": "25.b55fe1d287227ca97aab219bb249b8ab.315360000.1798284651.282335-8574074",
"expires_in": 2592000,
"scope": "public wise_adapt",
"session_key": "9mzdDZXu3dENdFZQurfg0Vz8slgSgvvOAUebNFzyzcpQ5EnbxbF+hfG9DQkpUVQdh4p6HbQcAiz5RmuBAja1JJGgIdJI",
"access_token": "24.6c5e1ff107f0e8bcef8c46d3424a0e78.2592000.1485516651.282335-8574074",
"session_secret": "dfac94a3489fe9fca7c3221cbf7525ff"
}
若请求错误,服务器将返回的 JSON 文本包含以下参数:
error: 错误码;关于错误码的详细信息请参考下方鉴权认证错误码。
error_description: 错误描述信息,帮助理解和解决发生的错误。
以下为请求错误返回结果:
{
"error": "invalid_client",
"error_description": "unknown client id"
}
error
error_description
解释
invalid_client
unknown client id
API Key不正确
invalid_client
Client authentication failed
Secret Key不正确
C++ 代码
#include
#include
#include
#include
#include
#include
// callback function for curl
size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *userdata)
{
std::string *str = dynamic_cast<:string>((std::string *)userdata);
str->append((char *)ptr, size * nmemb);
return size * nmemb;
}
// get access token from server by get method
std::string getTokenKey() {
std::string url = "https://aip.baidubce.com/oauth/2.0/token";
std::string apikey = "这里更改为对应应用的 API Key";
std::string secritkey = "这里更改为对应应用的 Secrit Key";
std::map<:string std::string> params;
std::string response;
params["grant_type"] = "client_credentials";
params["client_id"] = apikey;
params["client_secret"] = secritkey;
// append url with parameters
for (auto it = params.begin(); it != params.end(); ++it) {
url += (it == params.begin() ? "?" : "&") + it->first + "=" + it->second;
}
CURL *curl = curl_easy_init();
struct curl_slist * slist = NULL;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); // set callback function
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // set var to receive return info from callback function
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, true);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl, CURLOPT_VERBOSE, false);
int status_code = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_slist_free_all(slist);
Json::Value obj;
if (status_code != CURLcode::CURLE_OK) {
obj["curl_error_code"] = status_code;
return obj.toStyledString();
}
// parse json string
JSONCPP_STRING error;
Json::CharReaderBuilder builder;
const std::unique_ptr<:charreader> reader(builder.newCharReader());
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
std::string access_token = obj["access_token"].asString();
return access_token;
}
// write messages to file
int write_string_to_file_append(const std::string & file_string, const std::string str)
{
std::ofstream OsWrite(file_string, std::ofstream::app);
OsWrite << str;
OsWrite << std::endl;
OsWrite.close();
return 0;
}
int main(int argc, char *argv[])
{
std::string tokenKey;
tokenKey = getTokenKey();
std::cout << "Token Key: " << tokenKey << "\n";
std::string filename = "tokenKey.db";
write_string_to_file_append(filename, tokenKey);
system("pause");
exit(EXIT_SUCCESS);
}
代码分析
std::string url = "https://aip.baidubce.com/oauth/2.0/token";
std::string apikey = "这里更改为对应应用的 API Key";
std::string secritkey = "这里更改为对应应用的 Secrit Key";
std::map<:string std::string> params;
std::string response;
params["grant_type"] = "client_credentials";
params["client_id"] = apikey;
params["client_secret"] = secritkey;
// append url with parameters
for (auto it = params.begin(); it != params.end(); ++it) {
url += (it == params.begin() ? "?" : "&") + it->first + "=" + it->second;
}
上面这段代码主要用于获取最终的请求 URL。因为这里使用的是 get 方法来获取 access token,所以需要将所有参数添加到 URL 中。params 用于存储请求参数,response 表示请求结果。for 循环则是将各个参数和 URL 使用 ? 和 & 连接起来。最终 URL 的一个示例如下:
https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=Va5yQRHlA4Fq5eR3LT0vuXV4&client_secret=0rDSjzQ20XUj5itV6WRtznPQSzr5pVw2&
CURL *curl = curl_easy_init();
struct curl_slist * slist = NULL;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); // set callback function
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // set var to receive return info from callback function
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, true);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl, CURLOPT_VERBOSE, false);
int status_code = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_slist_free_all(slist);
上面这段代码主要是对 URL 请求进行设置,并请求,结果通过回调函数返回给 response。关于这里面的部分函数的使用方法可以参考:The Easy interface
curl_easy_init 函数必须是第一个要调用的函数,并且它返回一个 CURL 类型的简易句柄,必须将该 CURL 简易句柄用作 easy 接口中其他函数的输入。
curl_easy_setopt 用来告诉libcurl如何表现。通过设置适当的选项,应用程序可以更改libcurl的行为。这里设置的几个参数解释如下:
options
说明
URL to work on
Custom HTTP headers
Callback for writing data
Data pointer to pass to the write callback
Do not install signal handlers
Verify the SSL certificate
Verify the host name in the SSL certificate
Display verbose information
更多 Curl 的 options 参考 curl_easy_setopt。
// callback function for curl
size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *userdata)
{
std::string *str = dynamic_cast<:string>((std::string *)userdata);
str->append((char *)ptr, size * nmemb);
return size * nmemb;
}
上面这段代码是回调函数,一旦收到需要保存的数据,libcurl 就会立即调用此回调函数。对于大多数传输,此回调将被调用多次,每次调用都会传递另一块数据。ptr 指向传递的数据,该数据的大小为 nmemb;大小始终为 1。关于该函数的使用说明可以参考 CURLOPT_WRITEFUNCTION explained 和 getinmemory.c。
Json::Value obj;
if (status_code != CURLcode::CURLE_OK) {
obj["curl_error_code"] = status_code;
return obj.toStyledString();
}
// parse json string
JSONCPP_STRING error;
Json::CharReaderBuilder builder;
const std::unique_ptr<:charreader> reader(builder.newCharReader());
reader->parse(response.data(), response.data() + response.size(), &obj, &error);
std::string access_token = obj["access_token"].asString();
上面这段代码主要将字符串转换为 Json 格式,然后获取 access_token 值。
另外我们在调用接口获取到 access token 后,可以将其存储到文件中,因为每个 access token 的有效期为 30 天,所以可以重复使用直到过期。以下代码为将字符串写入文件和从文件读入。
int writeFile(const std::string & fileString, const std::string &str) {
std::ofstream out(fileString, std::ios::binary);
if (out.is_open()) {
out << str;
out.close();
}
return 0;
}
int readFile(const std::string & fileString, std::string &str) {
std::ifstream in(fileString);
if (!in.is_open()) {
str = "";
return -1;
}
char buffer[256];
while (!in.eof()) {
in.getline(buffer, sizeof(buffer));
}
str = buffer;
return 0;
}