自从sina微博oauth2出来以后,第三方集成开发简单了很多.Oauth2不像oauth1一样需要后台httpclient请求那么麻烦,一切都可以在前台使用ajax实现了.很多人觉得蹊跷,对于一个第三方应用,如何不走后台,而在...
自从sina微博oauth2出来以后,第三方集成开发简单了很多.Oauth2不像oauth1一样需要后台httpclient请求那么麻烦,一切都可以在前台使用ajax实现了.很多人觉得蹊跷,对于一个第三方应用,如何不走后台,而在前台使用ajax,来获取accesstoken?又如何向sina发起get或post请求?这其中最难解决的问题当属跨域问题.这篇文章将彻底解决这些疑问.
OAuth
OAuth为一种授权认证机制.它牵扯到服务提供方(sinaweibo),用户,第三方应用(比如糗事百科).第三方应用想方便用户可以在它的应用里面直接访问自己的weibo信息,还可以把第三方的信息发送到weibo,比如转发糗事到sinaweibo.实现这个功能,首先需要第三方应用拥有用户和sina的授权.在拿到授权以后,第三方应用才能实现集成功能.为什么不直接给第三方用户密码而用授权机制,这种问题就不罗嗦了.
拿糗百为例,授权的过程为:
1,糗百打开一个window或iframe,location指向weibooauth授权界面.
2,weibo授权界面是在sina的域名下,是由sina的服务器控制的.授权界面要求用户填写sina的账户,并确认授权.
3,用户确认以后,请求发回给sina.sina产生一个accesstoken,并发回给糗百.OAuth1使用httpclient,OAuth2使用跨域方法.
4,糗百保留用户的accesstoken.
5,用户在糗百发起分享糗事到sina,糗百将糗事和accesstokent一并发给sina.OAuth1使用httpclient,OAuth2使用跨域方法.
以上就是授权过程.OAuth1使用类似httpclient的方法与sina交流.这没有什么疑问,我就不多讲了.accesstoken的产生和验证也不是本文要讨论的.本文要讨论的是,OAuth2中实现的疑惑.第三方应用是如何通过跨域方法拿到sina的accesstoken的?它又是如何通过跨域方法向sina发起get或post请求的?
window.postMessage与window.name
在讲sina的框架的之前,先熟悉两个常见的跨域方法.window.postMessage和window.name.
两个window或iframe之间,只要能拿到对方window的句柄,就可以向对方发送消息.方法为
1otherWindow.postMessage(message,targetOrigin);
如果对方注册了事件监听就可以收到message.
1if(window.addEventLister){
2window.addEventLister("message",function(message){});
3}else{
4window.attachEvent("onmessage",function(message){});
5}
通过此种方法,可以在两个不同域的window之间相互传递信息.
因低于IE8的IE浏览器是不支持postMessage,所以还可以使用window.name来传值.如果写的复杂,则可以使用window.name来模拟postMessage的功能.window.name的局限是,只有同域下才能相互访问window.name,所以使用window.name来跨域传值,要花费不少功夫.
window.name跨域最典型的方法是使用代理页面.比如domainA下有a,domainB下有b.a如何传值给domainB呢?a可以先创建一个iframe,然后赋予新iframe的contentWindow.name一个message.然后,将iframe的src指向domainB的代理页面b-proxy.这时候,b可以访问b-proxy的window.name获取message.监听window.name变化可以使用setInterval来轮询.
sinaweibo就是使用的上面方法.首选postMessage,如果不支持postMessage,则使用window.name.由于其js写的太复杂,压缩以后更加难看.所以我一直没看明白它是如何使用window.name的,但原理跟上面是一样的.
Weibooauth2框架
1,第三方应用首先在sina处申请SDK的使用.从而获得一个JS的引用地址.
2,第三方将JS的引用放入自己的页面,看黄色的"Weibo".
3,(授权过程没有画在图上)当第三方想得到用户授权的时候,调用WB2.login()函数.weibojs弹出一个window,window的location指向sina的oauth2资源.用户在window中输入用户密码,提交到sina,sina返回一个自关闭页面.自关闭页面在关闭前,会将带回来的accesstoken用window.opener.postMessage()的方法传回给weibojs所注册的message监听方法.然后weibojs得到accesstoken,将其存入第三方应用的cookie下.
4,第三方应用开始通过Weibojs向sina发起资源请求.
5,Weibojs首先创建一个iframe,iframe中引入了sinaclientjs.此iframe跟第三方页面同源.
6,Clientjs又创建了一个iframe,iframe的src指向sinaxd.jsp,并引入了sina的xd.js.
7,xd.js跟sina同源,xd.js发起带有accesstokent的XMLHttpRequest,从sina得到资源.
8,xd.js通过postMessage或者window.name的方式回传给正在监听等待的clientiframe.
9,clientiframejs再回传给weibojs.
10,weibojs调用第三方页面的callback函数,并把获得的资源传给它.
11,callback分析资源,显示在页面上.
为什么使用clientiframe?去掉它好像也可以的样子.我说下对clientiframe的猜想,sina不想暴露太多的接口.weibojs就像接口,尽量短小精悍.将复杂的逻辑处理放在动态创建的clientjs里面.其次,由于某些低版本的浏览器不支持postMessage,所以需要使用window.name来模拟postMessage功能,需要使用代理,我怀疑clientiframe就起到一个代理功能.不过他们的代码本身写的就结构庞大,使用了大量的闭包,再用工具对js压缩了一遍,实在是看不清楚他们如何使用window.name的.万变不离其宗吧.