目標(biāo)
現(xiàn)在各大網(wǎng)站均可以使用一些已有其它賬戶(微信骇钦、微博等)登錄消痛,而不需要為其申請賬戶和密碼侥猬,其中使用的授權(quán)協(xié)議有一種是OAuth谆吴。
以下將演示如何使用C++ REST SDK和OAuth2.0協(xié)議仗岸,實現(xiàn)一個console客戶端谷遂,進行Windows Live身份驗證忧勿,并獲取一些用戶的基本信息挟秤。
OAuth2.0
關(guān)于OAuth的基本知識可以閱讀以下鏈接的內(nèi)容:
- 理解OAuth 2.0
- The OAuth 2.0 Authorization Framework
- Introducing OAuth 2.0
- Choosing an OAuth Type for Your API
準(zhǔn)備環(huán)境
本文開發(fā)環(huán)境為Visual Studio 2015,C++ REST SDK;
使用Vcpkg安裝C++ REST SDK
:
//下載vcpkg
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
//構(gòu)造
powershell -exec bypass scripts\bootstrap.ps1
//整合到Visual Studio 2015
vcpkg integrate install
//安裝C++ REST SDK
vcpkg install cpprestsdk
客戶端準(zhǔn)備
編寫客戶端使用OAuth授權(quán)時粘昨,需要到對應(yīng)的服務(wù)商申請客戶端ID和密鑰垢啼,一般在谷歌中搜索類似" Windows Live OAuth2"即可得到對應(yīng)的服務(wù)商說明;
我們需要查閱服務(wù)商的開發(fā)者文檔來查看其是否支持OAuth张肾,如何通過其進行授權(quán)芭析,譬如微軟對應(yīng)的 OAuth 2.0即描述了其對OAuth2.0的支持,以及開發(fā)說明;而Registering Your Application with Windows Live則描述了如何注冊客戶端到"Windows Live"捌浩。
實現(xiàn)流程
采用的是客戶端授權(quán)模式中的授權(quán)碼模式
放刨,授權(quán)流程如下:
客戶端(Windows Live 應(yīng)用程序)配置
- 應(yīng)用程序ID
- 應(yīng)用程序密碼
- 平臺:Web
- 重定向URI:
http://localhost:8890
- 勾選Live SDK支持
如何實現(xiàn)
OAuth2.0配置信息
C++ REST SDK中關(guān)于OAuth2的所有配置及最終獲取到的訪問令牌均存儲在oauth2_config
中,構(gòu)造時需要的信息有:
- client_key:客戶端ID
- client_secret:客戶端密碼
- auth_endpoint:認(rèn)證服務(wù)器端口
- token_endpoint:令牌服務(wù)器端口
- redirect_uri:重定向URI
- scope :授權(quán)請求范圍
構(gòu)造示例如下:
static const utility::string_t s_live_key(U("40654b2b-feda-4733-8499-584017f37c4e"));
static const utility::string_t s_live_secret(U("zL4fsEUOTHr8b8Ppm6bJhM9"));
oauth2_config config(s_live_key,
s_live_secret,
U("https://login.live.com/oauth20_authorize.srf"),
U("https://login.live.com/oauth20_token.srf"),
U("http://localhost:8890/"));
導(dǎo)向認(rèn)證服務(wù)器
客戶端需要構(gòu)造申請認(rèn)證的URI尸饺,并將用戶導(dǎo)向認(rèn)證服務(wù)器进统;
構(gòu)造申請認(rèn)證所需的URI,可以直接使用oauth2_config
的build_authorization_uri
方法浪听;
打開瀏覽器的實現(xiàn)如下:
//used to open browser
#include <Windows.h>
#include <shellapi.h>
static void open_browser(utility::string_t auth_uri)
{
auto r = ::ShellExecuteA(nullptr,"open",
conversions::utf16_to_utf8(auth_uri).c_str(),NULL,NULL,SW_SHOWNORMAL);
}
對應(yīng)流程實現(xiàn)如下:
void open_browser_auth()
{
auto auth_uri(m_oauth2_config.build_authorization_uri(true));
ucout<<"Opening browser in URI:" << std::endl;
ucout<<auth_uri<<std::endl;
open_browser(auth_uri);
}
獲取用戶授權(quán)碼
當(dāng)用戶給予授權(quán)后螟碎,認(rèn)證服務(wù)器會將用戶導(dǎo)向客戶端事先指定的“重定向URI”并附上授權(quán)碼,我們需要創(chuàng)建一個臨時的HTTP Listener來監(jiān)控返回的信息拿到授權(quán)碼:
一個簡單的HTTP Listener實現(xiàn)如下:
class OAuth2_listener
{
public:
OAuth2_listener(uri listen_uri):
m_listener(new http_listener(listen_uri))
{
m_listener->support([this](http_request request)->void {
//處理HTTP 請求
if (request.request_uri().path() == U("/") &&
request.request_uri().query() != U(""))
{
m_lock.lock();
//處理返回的授權(quán)碼,并設(shè)置為完成
m_tce.set(true);
request.reply(status_codes::OK, U("Ok."));
m_lock.unlock();
}
else
{
request.reply(status_codes::NotFound, U("Not found"));
}
});
m_listener->open().wait();//打開HTTP 監(jiān)聽
}
~OAuth2_listener()
{
m_listener->close().wait();//關(guān)閉HTTP Listener
}
pplx::task<bool> listen_for_code()
{
return pplx::create_task(m_tce); //等待認(rèn)證服務(wù)器的授權(quán)碼
}
private:
std::unique_ptr<http_listener> m_listener; //HTTP監(jiān)聽
pplx::task_completion_event<bool> m_tce;//收到授權(quán)碼的確認(rèn)事件
std::mutex m_lock;
};
使用OAuth2_listener.listen_for_code().get()
即可得到授權(quán)碼迹栓。
獲取訪問令牌
拿到授權(quán)碼之后掉分,則需要向認(rèn)證服務(wù)器申請令牌;在oauth2_config
中有方法實現(xiàn)了這樣的操作:
其操作步驟是獲取URI中的CODE,然后請求令牌酥郭,并將獲取的令牌保存到oauth2_config
中,調(diào)整HTTP Listener中處理授權(quán)碼的部分:
//處理返回的授權(quán)碼
//m_tce.set(true);
m_config.token_from_redirected_uri(request.request_uri()).then(
[this, request](pplx::task<void> token_task)->void {
try
{
token_task.wait();
m_tce.set(true);
}
catch (const oauth2_exception& e)
{
ucout << "Error: " << e.what() << std::endl;
m_tce.set(false);
}
catch (const http_exception& he)
{
ucout << "Error: " << he.what() << std::endl;
m_tce.set(false);
}
});
經(jīng)過這樣的處理之后华坦,訪問令牌就保存到了oauth2_config
中。
集成授權(quán)流程
經(jīng)過上述操作不从,即可拿到令牌惜姐,而在http_client_config
中有接口set_oauth2
可以將之前的存有令牌的oauth2_config
保存到客戶端配置中,之后就可以按照常規(guī)的HTTP/HTTPS操作獲取用戶信息了:
public:
http_client_config m_http_config;
oauth2_config m_oauth2_config;
std::unique_ptr<OAuth2_listener> m_listener_;
void work()
{
if (!m_oauth2_config.client_key().empty() &&
!m_oauth2_config.client_secret().empty())
{
if (!m_oauth2_config.token().is_valid_access_token())
{
if (m_listener_->listen_for_code().get())
{
m_http_config.set_oauth2(m_oauth2_config);//保存令牌及信息到http配置
}
else
{
//授權(quán)失敗
}
}
else
{
//令牌已有效
}
//發(fā)起獲取用戶信息請求
}
else
{
//沒有有效的客戶端信息
}
}
獲取用戶信息
以下是獲取Windows Live
上用戶信息的簡單示例:
http_client api(U("https://apis.live.net/v5.0/"),m_http_config);
ucout << "Requesting account information:"<<std::endl;
ucout << api.request(methods::GET,U("me")).get().extract_json().get() << std::endl;
輸出結(jié)果如圖:
收獲
上述源代碼主要來自于C++ REST SDK
中的OAuth2.0示例椿息,但是在具體分析和使用的過程中還是遇到了很多問題:申請Application時的配置歹袁,無法獲取令牌,運行時崩潰等等寝优;
為了定位無法獲取令牌的問題条舔,重寫了一遍 token_from_redirected_uri
,梳理了實現(xiàn)流程乏矾,最終發(fā)現(xiàn)是設(shè)置了HTTP Client的proxy所致孟抗,最新的代碼應(yīng)該不存在這個問題。
實際上OAuth2就是通過HTTP進行多次交互獲取訪問令牌钻心,C++ REST SDK
提供了HTTP通信功能夸浅,自己也可以完成整個OAuth2的授權(quán)流程。