自從sina微博oauth2出來之後, 第三方集成開發簡單了不少. Oauth2不像oauth1同樣須要後臺httpclient請求那麼麻煩, 一切均可以在前臺使用ajax實現了. 不少人以爲蹊蹺, 對於一個第三方應用, 如何不走後臺,而在前臺使用ajax, 來獲取access token? 又如何向sina發起get或post請求? 這其中最難解決的問題當屬跨域問題. 這篇文章將完全解決這些疑問. javascript
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
在講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的,但原理跟上面是同樣的. 併發
首先看圖
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的. 萬變不離其宗吧.