百度 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> 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; } 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; 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); curl_easy_setopt (curl, CURLOPT_WRITEDATA, &response); 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 (); } 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; } 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; 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); curl_easy_setopt (curl, CURLOPT_WRITEDATA, &response); 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的行为。这里设置的几个参数解释如下:
更多 Curl
的 options
参考 curl_easy_setopt 。
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 (); } 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 ; }