Sina微博OAuth2框架解密

自从sina微博oauth2出来之后, 第三方集成开发简单了不少. Oauth2不像oauth1同样须要后台httpclient请求那么麻烦, 一切均可以在前台使用ajax实现了. 不少人以为蹊跷, 对于一个第三方应用, 如何不走后台,而在前台使用ajax, 来获取access token? 又如何向sina发起get或post请求? 这其中最难解决的问题当属跨域问题. 这篇文章将完全解决这些疑问. javascript

OAuth

OAuth为一种受权认证机制. 它牵扯到服务提供方(sina weibo), 用户, 第三方应用(好比糗事百科).  第三方应用想方便用户能够在它的应用里面直接访问本身的weibo信息,还能够把第三方的信息发送到weibo, 好比转发糗事到sina weibo. 实现这个功能, 首先须要第三方应用拥有用户和sina的受权. 在拿到受权之后,第三方应用才能实现集成功能. 为何不直接给第三方用户密码而用受权机制, 这种问题就不罗嗦了. html

拿糗百为例,受权的过程为: 
1, 糗百打开一个window或iframe, location指向weibo oauth受权界面.
2, weibo受权界面是在sina的域名下,是由sina的服务器控制的. 受权界面要求用户填写sina的帐户,并确认受权.
3, 用户确认之后, 请求发回给sina. sina产生一个access token, 并发回给糗百. OAuth1使用httpclient, OAuth2使用跨域方法.
4, 糗百保留用户的access token.
5, 用户在糗百发起分享糗事到sina, 糗百将糗事和access tokent一并发给sina. OAuth1使用httpclient, OAuth2使用跨域方法. java

以上就是受权过程. OAuth1使用相似httpclient的方法与sina交流. 这没有什么疑问,我就很少讲了. access token的产生和验证也不是本文要讨论的. 本文要讨论的是, OAuth2中实现的疑惑. 第三方应用是如何经过跨域方法拿到sina的access token的? 它又是如何经过跨域方法向sina发起get或post请求的? ajax

window.postMessage与window.name

在讲sina的框架的以前,先熟悉两个常见的跨域方法. window.postMessage和window.name. 跨域

两个window或iframe之间,只要能拿到对方window的句柄,就能够向对方发送消息. 方法为 浏览器

otherWindow.postMessage(message, targetOrigin);

若是对方注册了事件监听就能够收到message. 服务器

if (window.addEventLister) {
  window.addEventLister("message", function(message){});
} else {
  window.attachEvent("onmessage", function(message){});
}
经过此种方法,能够在两个不一样域的window之间相互传递信息.

因低于IE8的IE浏览器是不支持postMessage,因此还可使用window.name来传值. 若是写的复杂,则可使用window.name来模拟postMessage的功能. window.name的局限是,只有同域下才能相互访问window.name,因此使用window.name来跨域传值,要花费很多功夫.  cookie

window.name跨域最典型的方法是使用代理页面. 好比domain A下有a.html, domain B下有b.html. a.html如何传值给domain B呢? a.html能够先建立一个iframe, 而后赋予新iframe的contentWindow.name一个message. 而后,将iframe的src指向domain B的代理页面b-proxy.html. 这时候,b.html能够访问b-proxy.html的window.name获取message. 监听window.name变化可使用setInterval来轮询. 闭包

sina weibo就是使用的上面方法. 首选postMessage, 若是不支持postMessage, 则使用window.name.  因为其js写的太复杂,压缩之后更加难看.因此我一直没看明白它是如何使用window.name的,但原理跟上面是同样的. 并发

Weibo oauth2 框架

首先看图

1,  第三方应用首先在sina处申请SDK的使用. 从而得到一个JS的引用地址. 
2,  第三方将JS的引用放入本身的页面, 看黄色的"Weibo javascript".
3,  (受权过程没有画在图上) 当第三方想获得用户受权的时候, 调用WB2.login()函数. weibo js弹出一个window, window的location指向sina的oauth2资源. 用户在window中输入用户密码,提交到sina, sina返回一个自关闭页面. 自关闭页面在关闭前,会将带回来的access token用window.opener.postMessage()的方法传回给weibo js所注册的message监听方法. 而后weibo js获得access token, 将其存入第三方应用的cookie下.
4,  第三方应用开始经过Weibo js向sina发起资源请求.
5,  Weibo js首先建立一个iframe, iframe中引入了sina client js. 此iframe跟第三方页面同源.
6,  Client js又建立了一个iframe, iframe的src指向sina xd.jsp, 并引入了sina的xd.js. 
7,  xd.js跟sina同源, xd.js发起带有access tokent的XMLHttpRequest, 从sina获得资源.
8,  xd.js经过postMessage或者window.name的方式回传给正在监听等待的client iframe.
9,  client iframe js再回传给weibo js.
10, weibo js调用第三方页面的callback函数,并把得到的资源传给它.
11, callback分析资源,显示在页面上.

为何使用client iframe? 去掉它好像也能够的样子. 我说下对client iframe的猜测, sina不想暴露太多的接口. weibo js就像接口, 尽可能短小精悍. 将复杂的逻辑处理放在动态建立的client js里面. 其次,因为某些低版本的浏览器不支持postMessage, 因此须要使用window.name来模拟postMessage功能, 须要使用代理,我怀疑client iframe就起到一个代理功能. 不过他们的代码自己写的就结构庞大,使用了大量的闭包,再用工具对js压缩了一遍,实在是看不清楚他们如何使用window.name的. 万变不离其宗吧.

相关文章
相关标签/搜索