纯 JavaScript 实现的 OAuth 认证
英文原文: JavaScript oAuth 编译: oschina
引言
现在,很多的应用程序都在使用HTML和JavaScript, 这是一个非常明智的选择,让你跟上目前的趋势. 一些主要实体工具因为客户端验证和授权等原因提供了API. 当前网站对于验证的一个广受欢迎的功能是”单点登录”. 这让用户可以通过其它一些社交媒体网站上的身份认证直接登录你的网站. 这篇文章介绍用纯JavaScript通过一个简单的方法让你从三个身份认证提供者上登录到你的网站.
背景
为了使用身份认证提供者的API, 你要在他们的网站上创建一个应用,完成后它将提供一个API key让你使用.
- 谷哥 API: https://code.google.com/apis/console/
- Facebook: https://developers.facebook.com/apps
- Twitter: https://dev.twitter.com/apps
使用这些代码
如果你不打算长篇阅读,你可以去直接下载源代码文件然后随便你怎么使用/修改。
首先,我们包含一些文件在我们的HTML页面的头部。
<!--Register Jquery--> <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script src="http://code.jquery.com/jquery-migrate-1.1.0.js"></script> <!--Register Google Api--> <script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script>
最近的时间,Twitter已经废弃使用可能会导致开发者变得畏畏缩缩的单点客户端认证。然而,感谢YUI(由Yahoo!提供的API)我们能够使用Javascript这样去模拟一个服务端请求到Twitter。所以我们也可以把YUI的库包含在头部。
<!--Register YUI Api--> <script type="text/javascript" src="http://yui.yahooapis.com/3.3.0/build/yui/yui-min.js"></script>
我们现在创建DOM元素来触发请求。这些元素可以是你的任意选择。
<div id="fbLogin" onclick="">Facebook</div> <div id="twitterlogin">Twitter</div> <div id="googlelogin">Google</div>
现在我们看下脚本。我们从Facebook的登陆开始。
我们要创建的第一个函数用来向 Facebook oAuth API 发送初始化请,函数将发送客户端 ID(由你的Facebook 应用给出),重定向 URL (必须同你注册应用时填写的一样),还有请求类型(此处是访问令牌)。访问令牌是用户验证所必须的,并也会用来访问 Facebook 的图形 API,以及获取用户个人信息。
var appID = <YOUR CLIENT ID>; function fbLogin() { var path = 'https://www.facebook.com/dialog/oauth?'; var queryParams = ['client_id=' + appID, 'redirect_uri=' + window.location, 'response_type=token']; var query = queryParams.join('&'); var url = path + query; window.location.replace(url); }
相应将是一个附加形如 “#access_token=…….” 的哈希字符串的访问令牌的到你网站的重定向。要使用访问令牌,我们现在需要一个检查 URL 中访问令牌的函数。同时此函数也将对 Facebook 图形 API 发送一个带有访问令牌和一个以参数形势接受用户信息的回调函数的请求。
function checkFbHashLogin() { if (window.location.hash.length > 3) { var hash = window.location.hash.substring(1); if(hash.split('=')[0] == 'access_token') { var path = "https://graph.facebook.com/me?"; var queryParams = [hash, 'callback=displayUser']; var query = queryParams.join('&'); var url = path + query; //use jsonp to call the graph var script = document.createElement('script'); script.src = url; document.body.appendChild(script); } } }
第三步,我们的回调函数会将用户信息转化为我们可见的形势。
function displayUser(user) { setTimeout(function () { }, 1000); if (user.id != null && user.id != "undefined") { //Do Stuff //You have access to user id, name, username, gender etc. //For more info visit https://developers.facebook.com/docs/ // reference/login/public-profile-and-friend-list } else { alert('user error'); } }
最后,我们需要一个函数调用的触发器,且我们也需要一个在页面加载的时候检查哈希字符串的函数。
$(function () { checkFbHashLogin(); $('#fbLogin').click(function () { fbLogin(); }); })
现在我们已完成了Facebook的身份验证。
首先,我将给出一个带有解释运作的注释的函数。如前所述,我们需要谷歌的客户端ID,我们的应用必须使用 API 密钥。
var clientId = <YOUR CLIENT ID>; var apiKey = <YOUR API KEY>; var scopes = 'https://www.googleapis.com/auth/plus.me'; // Our first function is used to set the api key and // is run once the google api is loaded in the page header. function handleClientLoad() { gapi.client.setApiKey(apiKey); } //Gets the result after the authorization and if successful, //it makes the api call to get the //user's information. function handleAuthResult(authResult) { if (authResult && !authResult.error) { makeApiCall(); } } //Make api call on button click to authorize client function handleAuthClick(event) { gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: false }, handleAuthResult); return false; } // Load the API and make an API call. Display the results on the screen. function makeApiCall() { gapi.client.load('plus', 'v1', function () { var request = gapi.client.plus.people.get({ 'userId': 'me' }); request.execute(function (resp) { //Do Stuff //You have access to user id, name, display name, gender, emails, etc. //For more info visit https://developers.google.com/+/api/latest/people#resource }); }); } $(function () { var authorizeButton = document.getElementById('googlelogin'); authorizeButton.onclick = handleAuthClick; })
这样就完成了谷歌的认证。
正如之前所说,Twitter API 并不允许纯粹的客户端验证。因此需要使用 YUI 库。此库使用雅虎查询语言(YQL)制定对Twitter API 的服务器端请求。
例如,应用中可能使用的一条 YQL 如下:
select * from twitter.oauth.requesttoken where oauth_callback="<YOUR WEBSITE URL>";
你可以看到,select 语句同其它查询语言的 select 语句一样。select 语句的条件参数(oauth_callback)是你 Twitter 应用所注册的 URL。由于我们选择 URL 匹配,我们并不需要客户端ID。由于我们使用 JavaScript,我们可以使用 window.location 获取当前 URL 用于查询(在代码中已处理)。本质上,我们无需关注 YUI 和 YQL。因此,让我们看看 YUI 库的实现,以及它是如何调用 Twitter API 的。
首先是初始化 YUI 对象。使用此对象,我们将设定所用的模块以及它们的地址。在这里将使用 Twitter 和 YQL 模块。
YUI({ combine: false, filter: "raw", debug: false, modules: { 'Twitter': { fullpath: 'js/twitter.js' }, 'myYQL': { fullpath: 'js/yql.js', requires: ['jsonp', 'jsonp-url'] } } })
下一步,我们将告知 YUI库我们将使用资源库中的哪个组件,并提供回调函数。我们通过调用 YUI 的 “use” 函数完成此操作,只需添加到初始化后的 YUI 对象之后。
YUI({ ... }).use('Twitter', 'gallery-storage-lite', 'myYQL', 'node', "event", function (Y) {});
回调函数将在 YUI 库初始化完成并选定组件之后调用,我们将所有处理 Twitter API 用户验证的代码放置其中。首先要做的是获取触发Twitter 用户验证的元素,并将点击事件绑定的到一个函数上。这个函数将进行第一次 API 调用,获取 oAuth 请求令牌和密钥。这些将在一个查询字符串中传递到 Twitter 用户验证 API,oAuth 令牌验证将以同样的方式返回
var twtBtn = Y.one('#twitterlogin'); twtBtn.on('click', function (e) { Y.Twitter.call({ type: "request_token" }, function (tokens) { Y.log("step 1"); Y.log(tokens); Y.StorageLite.setItem('oauth_token', tokens.oauth_token); Y.StorageLite.setItem('oauth_token_secret', tokens.oauth_token_secret); window.setTimeout(function () { window.location = "https://twitter.com/oauth/authenticate?oauth_token=" + tokens.oauth_token + "&oauth_token_secret=" + tokens.oauth_token_secret; }, 10); }); });
下面几步将结合嵌套的调用和响应,知道我们获得最终的响应。首先我们将检查查询字符串以获取oAuth 令牌验证。接着令牌和验证将被传递给 YUI Twitter调用以获取访问令牌。最后,访问令牌将被发送到一个调用,以索取用户信息。
if (getQueryStringParameter('oauth_token')) { Y.StorageLite.setItem('oauth_token', getQueryStringParameter('oauth_token')); Y.StorageLite.setItem('oauth_verifier', getQueryStringParameter('oauth_verifier')); Y.Twitter.config({ oauth_token: getQueryStringParameter('oauth_token'), oauth_token_secret: getQueryStringParameter('oauth_token_secret') }); Y.Twitter.call({ type: "access_token" }, function (tokens) { Y.Twitter.config({ oauth_token: tokens.oauth_token, oauth_token_secret: tokens.oauth_token_secret }); Y.Twitter.call({ type: "credentials" }, function (user) { Y.Twitter.config({ screen_name: user.screen_name, user_id: user.id }); //Do Stuff //You have access to user id, name, screenname, description, etc. //For more info visit https://dev.twitter.com/docs/api/1.1/get/account/verify_credentials }); }); }
这将完成 Twitter 认证组件的所有请求。
//Supporting function to parse the query string function getQueryStringParameter(key, queryString) { var queryString = queryString || window.location.href; key = key.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); var regex = new RegExp("[\\?&]" + key + "=([^&#]*)"); var qs = regex.exec(queryString); if (qs) return qs[1]; else return false; }
两点
现今,单点登录的使用率正迅速增长,很多大型网站,如雅虎和Hotmail都允许用户使用单点登录进行验证。例如,在配置Google API时,你可以将其它供应商同你的 Google API 密钥绑定。值得注意的是,所有请求都将使用各个验证提供者应用所登记域名,同时你将需要使用正确的域名和URL网址。因此,应用在你的 Localhost 将无法正常工作。