(基础篇 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

更多 Curloptions 参考 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 explainedgetinmemory.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;
}
上一篇:PHP的通过CURL或file_get_contents请求第三方URL


下一篇:php使用curl实现get和post数据请求,并获取返回值