目标
现在各大网站均可以使用一些已有其它账户(微信、微博等)登录,而不需要为其申请账户和密码,其中使用的授权协议有一种是OAuth。
以下将演示如何使用C++ REST SDK和OAuth2.0协议,实现一个console客户端,进行Windows Live身份验证,并获取一些用户的基本信息。
OAuth2.0
关于OAuth的基本知识可以阅读以下链接的内容:
- 理解OAuth 2.0
- The OAuth 2.0 Authorization Framework
- Introducing OAuth 2.0
- Choosing an OAuth Type for Your API
准备环境
本文开发环境为Visual Studio 2015,C++ REST SDK;
使用Vcpkg安装C++ REST SDK
:
//下载vcpkg
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
//构造
powershell -exec bypass scripts\bootstrap.ps1
//整合到Visual Studio 2015
vcpkg integrate install
//安装C++ REST SDK
vcpkg install cpprestsdk
客户端准备
编写客户端使用OAuth授权时,需要到对应的服务商申请客户端ID和密钥,一般在谷歌中搜索类似" Windows Live OAuth2"即可得到对应的服务商说明;
我们需要查阅服务商的开发者文档来查看其是否支持OAuth,如何通过其进行授权,譬如微软对应的 OAuth 2.0即描述了其对OAuth2.0的支持,以及开发说明;而Registering Your Application with Windows Live则描述了如何注册客户端到"Windows Live"。
实现流程
采用的是客户端授权模式中的授权码模式
,授权流程如下:
客户端(Windows Live 应用程序)配置
- 应用程序ID
- 应用程序密码
- 平台:Web
- 重定向URI:
http://localhost:8890
- 勾选Live SDK支持
如何实现
OAuth2.0配置信息
C++ REST SDK中关于OAuth2的所有配置及最终获取到的访问令牌均存储在oauth2_config
中,构造时需要的信息有:
- client_key:客户端ID
- client_secret:客户端密码
- auth_endpoint:认证服务器端口
- token_endpoint:令牌服务器端口
- redirect_uri:重定向URI
- scope :授权请求范围
构造示例如下:
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/"));
导向认证服务器
客户端需要构造申请认证的URI,并将用户导向认证服务器;
构造申请认证所需的URI,可以直接使用oauth2_config
的build_authorization_uri
方法;
打开浏览器的实现如下:
//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);
}
对应流程实现如下:
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);
}
获取用户授权码
当用户给予授权后,认证服务器会将用户导向客户端事先指定的“重定向URI”并附上授权码,我们需要创建一个临时的HTTP Listener来监控返回的信息拿到授权码:
一个简单的HTTP Listener实现如下:
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();
//处理返回的授权码,并设置为完成
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 监听
}
~OAuth2_listener()
{
m_listener->close().wait();//关闭HTTP Listener
}
pplx::task<bool> listen_for_code()
{
return pplx::create_task(m_tce); //等待认证服务器的授权码
}
private:
std::unique_ptr<http_listener> m_listener; //HTTP监听
pplx::task_completion_event<bool> m_tce;//收到授权码的确认事件
std::mutex m_lock;
};
使用OAuth2_listener.listen_for_code().get()
即可得到授权码。
获取访问令牌
拿到授权码之后,则需要向认证服务器申请令牌;在oauth2_config
中有方法实现了这样的操作:
其操作步骤是获取URI中的CODE,然后请求令牌,并将获取的令牌保存到oauth2_config
中,调整HTTP Listener中处理授权码的部分:
//处理返回的授权码
//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);
}
});
经过这样的处理之后,访问令牌就保存到了oauth2_config
中。
集成授权流程
经过上述操作,即可拿到令牌,而在http_client_config
中有接口set_oauth2
可以将之前的存有令牌的oauth2_config
保存到客户端配置中,之后就可以按照常规的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
{
//授权失败
}
}
else
{
//令牌已有效
}
//发起获取用户信息请求
}
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;
输出结果如图:
用户信息收获
上述源代码主要来自于C++ REST SDK
中的OAuth2.0示例,但是在具体分析和使用的过程中还是遇到了很多问题:申请Application时的配置,无法获取令牌,运行时崩溃等等;
为了定位无法获取令牌的问题,重写了一遍 token_from_redirected_uri
,梳理了实现流程,最终发现是设置了HTTP Client的proxy所致,最新的代码应该不存在这个问题。
实际上OAuth2就是通过HTTP进行多次交互获取访问令牌,C++ REST SDK
提供了HTTP通信功能,自己也可以完成整个OAuth2的授权流程。
网友评论