愛奇藝是如何實現第三方登陸無刷新更新頁面的

需求背景

最近在研究 iqiyi 的登陸流程,發現他們的第三方登陸體驗挺好的。javascript

在目標頁面(如下目標頁面均指用戶當前瀏覽的頁面)點擊登陸,彈出登陸彈框,選擇第三方登陸,好比 qq,微信,點擊則新開頁面。php

在新頁面中登陸完畢,頁面會自動關閉,且剛剛那個頁面不刷新且自動進行狀態變動html

首先是頁面自動關閉

這個很簡單,經過 window.close() 便可實現java

可是問題是,第三方登陸頁通常不會本身在代碼中作這件事,畢竟通常登陸後是重定向到某個頁面(經過 ticket 寫入 cookie )而不是關閉當前頁面web

那麼如何關閉呢?其實也很簡單,讓目標頁面執行登陸頁的關閉便可安全

newWin = window.open("//open.weixin.qq.com")
// 在某個時機
newWin.close()
複製代碼

至於這個時機,就是登陸成功以後。微信

那麼目標頁面如何知道登陸成功了?websocket

你可能會想說能夠利用輪詢啊,長鏈接啊,再或者 websocket 咯cookie

這個是能夠實現,可是額外浪費了性能,其實在當前頁面(登陸完重定向的頁面)經過 window.opener 操做目標頁面dom

至於這東西是啥,你們應該都知道,不懂的看 MDN

因此這就要求,登陸完咱們最後重定向的頁面除了寫入 cookie 的做用,還有就是操做 window.opener

好比這是 iqiyi 第三方登陸後重定向的頁面 https://passport.iqiyi.com/oauth/closepage.php?fromurl=&from=29&isNew=0

其頁面代碼就是如下簡單的幾行

<html><head><script> //成功調用 document.domain="iqiyi.com"; window.opener.lib.__callbacks__._oAuthSuccess('http://passport.iqiyi.com/pages/user/success.action'); </script>

</head><body></body></html>
複製代碼

這裏須要將 domain 設爲一致,才能操做相同 domain 的 window

是的, www.iqiyi.com 的 domain 也被改成了 "iqiyi.com" 了,一樣的, iqiyi 的其餘子域名好比 https://edu.iqiyi.com/ 等都改寫了 domain,使得全站都能順滑的實現無刷新登陸

最後看看代碼中的 window.opener.lib.__callbacks__._oAuthSuccess(xxx) ,其實做用也很簡單,一是關閉新開頁面,二是獲取用戶信息並更新狀態

這樣差很少就實現了咱們的需求,另外還有一點沒提到的是,寫入 cookie 的時候,domain iqiyi 寫的是二級域名 .iqiyi.com ,這樣其餘三級域名如 edu.iqiyi.com 才能共用 cookie

PS:在測試過程當中發現有 iqiyi 有兩個bug(截止 20/04/12),固然,普通用戶使用通常不會出現。。

  • 若登陸彈框關閉,第三方登陸成功後不會自動關閉頁面且不進行用戶信息請求,這個是因爲 _oAuthSuccess 實際上是調用的登陸窗口 iframe 中的方法
  • 手動調用 _oAuthSuccess 方法,頭像變成登陸後的默認頭像,此時其實並無登陸

那以上方案有什麼問題麼?

除了全站須要改造 document.domain 外,正常來講是沒有問題,可是可能會遇到不能修改 domain 的場景,你要是問我有哪些場景,emmmm...反正我就是爲了說個人新方案的,新方案就是 postMessage

我如今能想到的一個,好比最後一個重定向的最後一個頁面不是 .iqiyi.com域名,而是其餘頁面(多是爲了集團內多個產品複用吧),好比 util.baidu.com

www.iqiyi.com

newWin = window.open("//util.baidu.com")
window.addEventListener("message",(evt)=>{
    // 根據信息來源進行特殊處理,避免受其餘 postMessage 影響
    if(evt.origin === "https://util.baidu.com"){
        console.log(evt.data)
        newWin.close()
        // or
        // evt.source.close()
    }
})
複製代碼

util.baidu.com

window.opener.postMessage("test","*") //全部
複製代碼

能夠在 www.iqiyi.com 中看到輸出,且 util.baidu.com 自動關閉

固然該方案也有很差的地方,就是 postMessage 使用不當會致使安全問題,主要有如下2點

  1. postMessage 第二個參數 targetOrigin 設置爲 * 可能致使數據泄露
    好比使用了某個不安全的第三方登陸頁面,其對目標網站進行了重定向,好比變成 www.ihack.com ,第三方登陸後經過 postMessage 傳遞隱私信息,因爲設置的是 * ,致使 www.ihack.com 能夠收到隱私信息。固然你後面發現這個 ihack 網站不正常也沒用,由於數據已經泄露了。
    解決方案就是 targetOrigin 設置爲特定的地址好比 edu.iqiyi.como ,這就須要咱們將當前頁面做爲傳遞到重定向頁面
    舉例,像第三方登陸頁傳遞這樣的參數 -- &redirect_uri=https://passport.iqiyi.com?targetOrigin=edu.iqiyi.com , 第三方登陸後重定向到 passport 寫入 cookie ,再次重定向到 https://util.baidu.com?targetOrigin=edu.iqiyi.com ,再當前頁面讀取 targetOrigin 參數並傳遞 postMessage 。整個就是這樣的一個過程
    固然在本例中不須要傳遞隱私信息

  2. 接收 message 時請始終使用 origin 和 source 屬性驗證發件人的身份
    好比點擊打開了用戶評論的 url(某個黑客網站),其對目標頁面發起 postMessage ,而咱們若是在收到 message 時不進行驗證就執行危險操做時,可能就會致使安全問題
    另外,若是回調執行安全性依賴於消息的話(好比執行 eval(msg.data)),即便檢查經過,還須要對消息格式再一次檢查,避免因信任網站收到跨站腳本攻擊而致使本網站也遭到攻擊 固然在本例中只是簡單的關閉新開頁面及發送請求,沒有什麼危險操做,檢查下 origin 便可

總結

domain 適用於大部分情景,有全站改造 document.domain 的成本,且最後重定向頁面須要是相同的 domain

postMessage 適用於全部情景,對於安全問題須要額外處理

兩種方案各有千秋,看就具體的場景啦~

參考文檔

相關文章
相關標籤/搜索