(基础篇 03)C++ 获取 access token
百度 AIP 开放平台使用 OAuth2.0 授权调用开放 API,调用 API 时必须在 URL 中带上 access_token 参数。
请求 URL 数据格式
授权服务地址:https://aip.baidubce.com/oauth/2.0/token
请求参数如下:
- 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 <curl/curl.h>
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include <json/json.h>
// callback function for curl
size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *userdata)
{
std::string *str = dynamic_cast<std::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<std::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<Json::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<std::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 | 说明 |
---|---|
CURLOPT_URL | URL to work on |
CURLOPT_HTTPHEADER | Custom HTTP headers |
CURLOPT_WRITEFUNCTION | Callback for writing data |
CURLOPT_WRITEDATA | Data pointer to pass to the write callback |
CURLOPT_NOSIGNAL | Do not install signal handlers |
CURLOPT_SSL_VERIFYPEER | Verify the SSL certificate |
CURLOPT_SSL_VERIFYHOST | Verify the host name in the SSL certificate |
CURLOPT_VERBOSE | 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<std::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<Json::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;
}