跨域單點登陸方案實現

SSO英文全稱Single Sign On,單點登陸。當咱們搜索單點登陸的時候,會發現不少的文章,然而這些文章通常都是基於一種通用的場景描述,一般在各自的業務環境會更加複雜。在本篇文章,我將描述具體場景下實現單點登陸的方案。html

SSO英文全稱Single Sign On,單點登陸。SSO是在多個應用系統中,用戶只須要登陸一次就能夠訪問全部相互信任的應用系統。它包括能夠將此次主要的登陸映射到其餘應用中用於同一個用戶的登陸的機制。它是目前比較流行的企業業務整合的解決方案之一。前端

背景

企業發展初期,通常一個域名站點即可承載獨立業務。但隨企業對於新業務的探索,便都會申請一個新的域名用於這部分新業務的功能承載,一方面是爲了作區分,另外一方面是知足監管的要求。而且須要在這種模式下打通原先站點的用戶體系。新的獨立域名主要有兩種:webpack

  1. 原有站點是www.a.com,新獨立域名是new.a.com;
  2. 原有站點是www.a.com,新獨立域名是www.b.com;

對於第一種場景,通常咱們採用共享session的方式就能夠作到用戶在不一樣域名之間跳轉而無需重複登陸。具體實現主要是將cookies中關於用戶登陸態的sessionid的domain設置爲.a.comweb

這種場景較爲簡單,實現上作好新老模式之間的切換便可。由於默認狀況下sessionId的domian是www.a.com,若是以前已經訪問過www.a.com站點,且登陸的時候未清除掉domian爲www.a.com的sessionid,那麼訪問www.a.com的站點,瀏覽器會把兩個同名sessionId傳遞到服務端,因爲是key-value的形式,服務端沒法分辨哪一個是新的,哪一個是舊的,若是取了舊的,那麼就沒法獲取用戶此時登陸狀態。解決這個問題,只須要在設置sessionId的時候把原有domian爲www.a.com的置爲過時,或者用一個新的sessionId鍵便可。具體查看set-cookies介紹redis

這種場景在咱們的移動站點用的比較多。例如主站www.a.com,移動站點爲m.a.com這種場景。npm

#設置domian爲.a.com的sessionId
Set-Cookie: sessionId=a3fWa; Domain=.a.com; Secure; HttpOnly

#將domian爲www.a.com的sessionId置爲過時
Set-Cookie: sessionId=a3fWa; Domain=www.a.com; expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; HttpOnly
複製代碼

接下來咱們主要描述第二種場景。對於這種場景,主要要求是用戶無感知,須要作到如下幾點:跨域

  1. a站點登陸以後往b站點同步登陸態;
  2. 訪問b站點無需登陸頁面後,須要主動同步a站點登陸態;
  3. 訪問b站點須要登陸頁面時,須要跳轉到a站點作登陸態同步;

流程一:a站點登陸以後往b站點同步登陸態

登陸以後同步登陸態時序圖

以上是主動同步登陸態的時序圖。圖示中的ticket主要存放在redis中,你也能夠存放在其餘的存儲媒介甚至應用運行內存,可是須要注意的一點就是ticket應一次有效,用過以後須要清除掉。因爲這裏咱們的b站點也在本身的受控範圍,而且redis的讀寫性能也至關優越,因此a和b鏈接並讀取了同個redis。若是b站點不在受控範圍內,可在b站點後臺發起一個請求到a站點詢問ticket的有效狀態。具體流程以下:瀏覽器

  1. 用戶訪問a.com的登陸頁;
  2. 輸入用戶名密碼登陸,a.com後臺校驗用戶,成功以後生成a站點的sesion並生成一個ticket放入redis中;
  3. 登陸頁面登陸成功以後,拿到ticket往b.com發送一個跨域請求(JSONP或者Image);
  4. b站點獲取到ticket以後,檢驗在redis是否存在,存在着設置b站點session並刪除ticket;
  5. 跨域請求返回以後繼續其餘操做,如跳轉用戶中心,首頁等。

流程二:b站點無需登陸頁面主動同步a站點登陸態

b站點無需登陸頁面主動同步a站點登陸態時序圖

因爲a、b站點相互獨立,假設各自的session過時時間爲半小時,若是a站點一直處於訪問狀態,那麼session會一直續命下去,可是b站點因爲超過30分鐘沒有訪問,session狀態已通過期,這時候訪問b站點就會有這個場景了。具體流程以下:安全

  1. 用戶訪問b.com無需登陸頁面;
  2. 若是當前站點用戶未登陸,發起異步JSONP請求到a.com;
  3. 若是a.com未登陸,不作任何操做。若是已經登陸,跟上個流程同樣,生成ticket信息;
  4. 拿到ticket以後,請求b站點同步登陸狀態,b站點生成session;
  5. 同步成功以後主動從新刷新當前頁面。

流程三:b站點需登陸頁面主動跳轉到a站點作登陸態同步;

b站點需登陸頁面主動跳轉到a站點作登陸態同時序圖

同流程二,這個場景的出現也是由於長時間未訪問登陸b站點致使,與流程二不一樣的是,這個場景是302直接跳轉同步頁面的方式,由後臺直接判斷,適合後臺直出頁面,若是是純靜態頁面請使用流程二,具體流程以下:bash

  1. 用戶訪問b.com須要登陸頁面,返回302跳轉到a站點的狀態同步頁面;
  2. a站點狀態同步頁面判斷站點登陸狀態,未登陸狀態跳轉到a站點的登陸頁面,登陸流程同流程一,登陸成功以後跳轉b站點需登陸頁面;
  3. 已經登陸狀態作JSONP登陸狀態同步到b站點,b站點生成session;
  4. 同步b站點成功以後跳轉到b.com站點須要登陸頁面。

總結

以上流程主要在於實現SSO過程的針對各類場景的解決方案,根據經過發起方的不一樣又可分爲兩類,主動同步和被動同步。主動同步是向認證站點獲取ticket並同步自身登陸態,被動同步是由認證站點向當前站點同步登陸態。

通常網上的資料會有一個專門用做認證登陸的站點,好比a.com和b.com站點都從sso.a.com獲取認證狀態。其實在本文中就是把a站點用做sso站點了,原理上是一致的。

爲了這一套方案落地的時候,業務開發同事無需關注這部分實現細節並編寫相應的同步代碼,咱們把他寫入了總體框架裏面,主要作了一下兩件事:

  1. 封裝前端登陸腳本(支持npm引用,直接webpack打包),登陸以後去作主動站點同步,業務同事在須要登陸的場景,直接調用方法並在回調函數作其餘操做;
  2. 使用ejs或者pug等模板引擎,將流程二的同步過程放入通用模板,其餘頁面在通用模板基礎上建立。

更多關於sso認證方面的能夠參考OAuth2的流程,這套流程用於不可信站點之間的認證在安全方面會更加成熟些,也是目前微信採用的認證流程。

FYI:

單點登陸(SSO)看這一篇就夠了
SSO單點登陸三種狀況的實現方式詳解
微信網頁受權

相關文章
相關標籤/搜索