OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是OAUTH的授权不会使第三方触及到用户的帐号信 息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAUTH是安全的。同时,任何第三方都可以使用 OAUTH认证服务,任何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如 PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间,因而OAUTH是简易的。目前互联网很多服务如Open API,很多大头公司如Google,Yahoo,Microsoft等都提供了OAUTH认证服务,这些都足以说明OAUTH标准逐渐成为开放资源授权 的标准。
在官方网站的首页,可以看到下面这段简介:
An open protocolto allow secure API authorization in a simple and standard method from desktopand web applications.
大概意思是说OAUTH是一种开放的协议,为桌面程序或者基于BS的web应用提供了一种简单的,标准的方式去访问需要用户授权的API服务。OAUTH 类似于FlickrAuth、Google's AuthSub、Yahoo'sBBAuth、 Facebook Auth等。OAUTH认证授权具有以下特点:
1. 简单:不管是OAUTH服务提供者还是应用开发者,都很容易于理解与使用;
2. 安全:没有涉及到用户密钥等信息,更安全更灵活;
3. 开放:任何服务提供商都可以实现OAUTH,任何软件开发商都可以使用OAUTH;
OAuth的原理
我在图上分了四个步骤,下面是四步的讲解:
第一步:用户访问第三方网站,比如:就是你需要使用QQ进行登录的网站;
第二步:你点击QQ登录后,第三方网站将会连接并进行请求,比如:你点击登录后,第三方网站会跳转到QQ平台,提示你进行登录;
第三步:你要进行授权第三方网站对你的信息访问的一个权限,比如:当你QQ登录成功后,QQ会提示你,是否授权第三方Web访问你的用户基本信息或其他的资源信息,这时你点击授权即可;
第四步:授权后,第三方Web即可访问你刚才授权的资源信息,比如:你的QQ基本信息-头像、昵称、性别等。
通过这个原理图示及讲解(图是手工制作,有点草),相信大家都了解了OAuth这个原理的一个基本流程,若看不明白,你可以不用学习制作OAuth了,开个玩笑。(这步后,大家知道什么原理了,但还是无法知道OAuth究竟是如何实现认证的,别急,接着往下看)
1、OAuth 1.0的认证流程
这里直接讲解OAuth 1.0协议的认证机制(OAuth 2.0会在下一节中讲述),虽然现在很多平台都是遵循OAuth 2.0,但还是有开放OAuth 1.0平台的,比如:新浪微博、QQ1.0平台等。
在OAuth 1.0认证中会用到三个重要的Url:
第一个:Request Token Url,获取未授权的Token的Url;
第二个:User Authorization Url,请求用户对Token进行授权的Url;
第三个:Request Access Url,使用Token获取AccessToken的Url。
上面是认证流程中用到的三个Url,在下面的流程示意图中会体现到:
第一步:网站向认证平台请求一个未授权的Token,这个Request Token Url是前面说的第一个Url;
第二步:跳转至用户授权页面,提示用户进行登录,并进行授权,返回获得已授权的Token,用到的User Authorization Url是前面说的第二个Url;
第三步:通过已授权的Token,向认证平台请求Access Token(数据令牌),用到的Request Access Url是前面说的第三个Url,返回后到这步整个认证流程就结束了,最后一步,是通过数据令牌等参数,调用接口获取用户信息,不完全算认证的流程。(我喜欢简洁明了,认证流程就是这样,相信通过图示及讲解都能明白,若有不明白之处请留言)
2、QQ OAuth1.0认证中Url的调用及参数的传递
前面讲了OAuth1.0的机制原理及认证流程,这篇文章着重讲解QQ OAuth1.0认证中Url的调用、各参数的传递、注意事项。而因为现在QQ开发平台上,已经很少能找到OAuth1.0认证的说明开发文档了,采用新的2.0认证模式,所以简单讲述Url请求与返回参数的传递,并没有很详细深入地讲解,有什么问题大家可留言给我。
2.1、请求未授权的临时Token
新建一个页面为QQLogin.aspx,用来请求临时Token及跳转到用户授权页。下面是相关Url及参数介绍:
Request Token Url(请求临时Token的Url):http://openapi.qzone.qq.com/oauth/qzoneoauth_request_token
请求后,跳转至引导用户登录的Url:http://openapi.qzone.qq.com/oauth/qzoneoauth_authorize
第一个Url请求参数包含如下内容:(红色为必填、绿色为选填)
参数 |
含义 |
oauth_consumer_key |
申请QQ登录成功后,分配给网站的appid |
oauth_nonce |
随机字符串,所有oauth_nonce请使用int型值。 |
oauth_timestamp |
unix时间戳(从UTC时间1970年1月1日00:00:00到当前时刻的秒数,不同语言中如何获取请google/baidu之)。 注意第三方服务器时间与腾讯服务器时间相差不能超过5分钟。 |
oauth_version |
版本号,请固定使用1.0 |
oauth_signature_method |
签名方法,请固定使用HMAC-SHA1。 |
oauth_signature |
签名值,用来提高传输过程参数的防篡改性。 |
oauth_client_ip |
用户的IP地址(可选),int型 |
返回的参数有:oauth_token(临时令牌)、oauth_token_secret(临时密钥对应的令牌)
第二个Url需要传递的参数为:
参数 |
含义 |
oauth_consumer_key |
分配给网站的appid。 |
oauth_token |
上一步中,得到的oauth_token |
oauth_callback |
回调地址,即登录并授权后返回到你网站上的地址。 |
返回的参数有:
参数 |
含义 |
oauth_token |
已授权的token |
openid |
与APP通信的用户key,它和QQ号码一一对应,访问OpenAPI时必需。 同一个QQ号码在不同的应用中有不同的OpenID。 |
oauth_signature |
签名值。如果网站使用这一步返回的openid,则需要按规则生成签名值,并与该签名值比对,以验证openid以及来源的可靠性。 比对时生成签名值的规则:使用HMAC-SHA1算法,源串:openid+openid的timestamp(串中间不要添加'+'符号);密钥:oauth_consumer_secret。 |
timestamp |
openid的时间戳 |
oauth_vericode |
授权验证码。 |
2.2、请求Access Token数据令牌
上一步中,我们写了一个回调地址,并顺利得到一些参数,下面就是拿这些参数来进行下一步操作,首先,得到数据令牌(只有拿到数据令牌才可以调用接口获取用户信息)。
Request Access Url(获取Access Token请求Url):http://openapi.qzone.qq.com/oauth/qzoneoauth_access_token
Url请求参数包含如下内容:
参数 |
含义 |
oauth_consumer_key |
分配给网站的appid |
oauth_token |
已授权的的token,上一步返回的oauth_token |
oauth_nonce |
随机数 |
oauth_timestamp |
unix时间戳(从UTC时间1970年1月1日00:00:00到当前时刻的秒数,不同语言中如何获取请google/baidu之) |
oauth_version |
版本号,请固定使用1.0 |
oauth_signature_method |
签名方法,请固定使用HMAC-SHA1 |
oauth_signature |
签名值,用来提高传输过程参数的防篡改性。 |
oauth_vericode |
授权验证码,上一步返回的oauth_vericode |
oauth_client_ip |
用户的IP地址(可选),int型 |
返回的参数如下:
参数 |
含义 |
oauth_token |
具有访问权限的access_token |
oauth_token_secret |
access_token的密钥 |
openid |
在第一步和本步骤中都返回了openid。使用本步骤返回的openid,更为安全 |
timestamp |
openid的时间戳 |
oauth_signature |
针对openid的签名值 |
2.3、通过Access token(数据令牌)调用API接口,获取用户授权资源
请求Url:http://openapi.qzone.qq.com/user/get_user_info (这里默认写的是get_user_info接口)
请求参数:
参数 |
含义 |
oauth_consumer_key |
分配给网站的appid |
oauth_token |
上一步返回的oauth_token |
oauth_nonce |
随机数,int型 |
oauth_timestamp |
unix时间戳(从UTC时间1970年1月1日00:00:00到当前时刻的秒数,不同语言中如何获取请google/baidu之)。 |
oauth_version |
版本号,固定使用1.0 |
oauth_signature_method |
签名方法,固定使用HMAC-SHA1 |
oauth_signature |
签名值,用来提高传输过程参数的防篡改性。 |
openid |
上一步返回的openid。 |
oauth_client_ip |
用户的IP地址(可选),int型 |
好了,上面就是整个QQ OAuth1.0认证流程中Url参数的请求与返回说明,这OAuth1.0认证中,你会发现参数特别多,还有经过HMAC-SHA1加密、签名等操作,特别麻烦、繁琐,所以还是最好推荐使用OAuth 2.0认证协议进行接口开发。
3、OAuth2.0的认证流程
在OAuth2.0的处理流程,主要分为以下四个步骤:
1)得到授权码code
2)获取access token
3)通过access token,获取OpenID
4)通过access token及OpenID调用API,获取用户授权信息
上面是流程的大概四个步骤,在下面的流程示意图中会得到体现:
第一步:首先直接跳转至用户授权地址,即图示 Request User Url,提示用户进行登录,并给予相关资源授权,得到唯一的Auth code,这里注意的是code只有10分钟的有效期,对于安全考虑,相对于OAuth1.0省了一步获取临时的Token,并且有效期也进行了控制,比1.0认证简化了很多,并安全一些;
第二步:得到授权code后,这一步就是请求access token,通过图示 Request access url ,生成得到数据Token;
第三步:通过Access Token请求OpenID,OpenID是用户在此平台的唯一标识,通过图示Request info url 请求,然后得到OpenID;
第四步:通过第二步得到的数据Token、第三步得到的OpenID及相关API,进行请求,获取用户授权资源信息。
1. 请确保您的网站已经提交接入QQ登录的申请,并成功获取到appid和appkey。申请接入
2. 请在你的服务器上ping openapi.qzone.qq.com ,保证网站和Qzone的连接畅通。
网站需要下载“QQ登录”按钮图片,并按照UI规范将按钮放置在页面合适的位置。
按钮图标下载 按钮放置规范
1. 打开浏览器,访问如下地址(请将client_id,redirect_uri,scope等参数值替换为你自己的):
https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=[YOUR_APPID]&redirect_uri=[YOUR_REDIRECT_URI]&scope=[THE_SCOPE]
2. 如果用户已经有登录态,会弹出提示一个确认页。如果还没有登录,会弹出登录页,如下图所示:
3. 成功登录后,弹出授权框引导用户授权(仅在第一次成功登录,以及第一次访问某个未授权的OpenAPI时会出现授权页),如下图所示:
注意:
如果用户点击“跳过”,则跳转到回调地址,返回默认的头像、昵称和性别。
建议第三方应用控制授权项,即参数scope中只传入必须使用的OpenAPI名称。因为授权项越多,用户越有可能拒绝授权。
4. 如果用户点击“确认”授权,则成功跳转到指定的redirect_uri,并跟上Authorization Code(注意此code会在10分钟内过期)。
例如回调地址是:www.qq.com/my.php,则会跳转到:
http://www.qq.com/my.php?code=520DD95263C1CFEA0870FBB66E******
注意:
回调地址建议设置为网站首页或网站的用户中心。
1.发送请求到如下地址(请将参数值替换为你自己的,参数解释详见这里):
https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=[YOUR_APP_ID]&client_secret=[YOUR_APP_Key]&code=[The_AUTHORIZATION_CODE]&state=[The_CLIENT_STATE]&redirect_uri=[YOUR_REDIRECT_URI]
2. 即可获取到Access Token:
access_token=YOUR_ACCESS_TOKEN&expires_in=3600
可在回调URL对应的程序(见下文的示例代码)中接受请求,并完成获取Access Token的工作。
特别提示:
获取到的access token具有3个月有效期,用户再次登录时自动刷新。
第三方网站可存储access token信息,以便后续调用OpenAPI访问和修改用户信息时使用。
1. 发送请求到如下地址(请将access_token等参数值替换为你自己的):
https://graph.qq.com/oauth2.0/me?access_token=YOUR_ACCESS_TOKEN
2. 获取到用户OpenID,返回包如下:
callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
1. 建议网站在用户登录后,即调用get_user_info接口,获得该用户的头像、昵称并显示在网站上,使用户体验统一。
2. 调用其他OpenAPI,以访问和修改用户数据。所有OpenAPI详见API列表。
以调用get_user_info接口为例:
(1)发送请求到get_user_info的URL(请将access_token,appid等参数值替换为你自己的):
https://graph.qq.com/user/get_user_info?access_token=YOUR_ACCESS_TOKEN&oauth_consumer_key=YOUR_APP_ID&openid=YOUR_OPENID
(2)成功返回后,即可获取到用户数据:
{
"ret":0, "msg":"", "nickname":"YOUR_NICK_NAME", ...
}
//应用的APPID $app_id = "YOUR_APP_ID";//应用的APPKEY $app_secret = "YOUR_APP_KEY";//成功授权后的回调地址 $my_url = "YOUR_REDIRECT_URL";
//Step1:获取Authorization Code session_start(); $code = $_REQUEST["code"];
if(empty($code)) { //state参数用于防止CSRF攻击,成功授权后回调时会原样带回 $_SESSION['state'] = md5(uniqid(rand(), TRUE)); //拼接URL $dialog_url = "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" . $app_id . "&redirect_uri=" . urlencode($my_url) . "&state=" . $_SESSION['state'];echo(""); }
//Step2:通过Authorization Code获取Access Token if($_REQUEST['state'] == $_SESSION['state']) { //拼接URL $token_url = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&" . "client_id=" . $app_id . "&redirect_uri=" . urlencode($my_url) . "&client_secret=" . $app_secret . "&code=" . $code;
$response = file_get_contents($token_url); if (strpos($response, "callback") !== false) { $lpos = strpos($response, "("); $rpos = strrpos($response, ")"); $response = substr($response, $lpos + 1, $rpos - $lpos -1); $msg = json_decode($response); if (isset($msg->error)) { echo "error:
" . $msg->error; echo "msg :
" . $msg->error_description; exit; } }
//Step3:使用Access Token来获取用户的OpenID $params = array(); parse_str($response, $params);
$graph_url = "https://graph.qq.com/oauth2.0/me?access_token=" $params['access_token'];$str = file_get_contents($graph_url); if (strpos($str, "callback") !== false) { $lpos = strpos($str, "("); $rpos = strrpos($str, ")"); $str = substr($str, $lpos + 1, $rpos - $lpos -1); } $user = json_decode($str); if (isset($user->error)) { echo "error:
" . $user->error; echo "msg :
" . $user->error_description; exit; } echo("Hello " . $user->openid); } else { echo("The state does not match. You may be a victim of CSRF."); }?>