實現一個SSO系統

JayChen發表於2019-02-16

SSO全稱Single Sign On,單點登入.用於實現多系統的登入認證.說白了就是隻要一個賬號就可以訪問阿里旗下支付寶,淘寶, 天貓等網站.站在企業的角度這樣做有一個好處:只要有一個系統負責登入模組,其他的子系統就可以專注於自己系統的業務邏輯,需要的時候請求認證系統就可以.站在使用者的角度好處在於只要一個賬號就可以訪問其公司旗下所有產品.

SSO有多種實現方案,這次我們來分析一下其中的一種可行方案.並且擼起袖子程式碼實現一遍.其實現效果如下

  • 一個賬號可以同時訪問www.a.com跟www.b.com

  • 賬號在一個系統中退出,其他系統也同樣退出.

  • 如果需要,改動一點程式碼可以實現賬號在www.a.com登入之後訪問www.b.com不用進行賬戶密碼登入認證.

原理

在實現之前,我們來分析一下整個流程.

通常情況下,系統判斷使用者是否登入都是校驗session.在SSO架構中,判斷使用者是否登入仍然依靠session,但是session由SSO server維護, session_id由client保持,client直接訪問的卻是業務系統.

在這樣的情況下,要想能夠使用session進行登入驗證. 假設client訪問業務系統,業務系統取得其session_id,然後把session_id提交到SSO Server進行驗證.我們看到這裡的業務系統充當了一個proxy的角色,下文以proxy指代各個業務系統.

這種方式簡單粗暴,但是有一個很大的侷限性 : client持有的session_id由SSO Server釋出的,proxy跟SSO Server必須持有相同域名,不然proxy無法獲取到client的session_id.所以需要在這種思路上做一點改變.

client訪問proxy,proxy隨機生成一個token作為cookie儲存,並且SSO Server維護有一個以token為檔名的檔案,檔案儲存了client訪問SSO Server生成的session id.當proxy需要驗證client身份,只要獲取到token,到SSO Server訪問token檔案,獲取到session id.那麼久可以訪問到session資料.從而判斷該client是否已經登入.

總體設計思路如上,但是除了token,為了保證安全性,以及多個proxy之間不會產生衝突.一個完整的流程應該如下 :

  • client —> proxyA.proxyA為client生成一個隨機的token當做cookie.然後引導client重定向到SSO Server.其中引導client重定向到SSO Server應該包含如下資訊

    • token,由proxy生成的隨機值

    • command 即訪問SSO Server的目的,一般為SSO Server系統中的一個函式名.

    • returnUrl : 一般為client訪問proxyA的url,即SSO Server 會重新引導client返回當前頁面

    • 當前proxy的ID.proxy跟SSO Server之間約定好的一個唯一ID,並且每個唯一ID必須對應一個secret key.這個key只有當前proxy跟SSO Server知道.

    • checksum : 一個包含上述資訊和secret key的單項加密,用於SSO Server驗證引數是否被惡意修改.

  • 上述的重定向url應該類似www.sso.com?token=xxx&command=xxx&returnurl=xxx&proxy=proxyA&checksum=xxx.client被proxyA重定向到SSO Server之後,SSO校驗引數的合法性後為client生成一個session_id.並且建立一個以proxyID-token-checksum的命名的檔案,儲存當前client的session id.完成上述操作之後引導client重定向到returnUrl.這樣使用者的體驗不會受到影響.

  • client重定向到proxyA後可以進行登入操作,proxyA可以從cookie中獲取到token.client提交使用者名稱跟密碼,proxyA重新生成一個proxyID-token-checksum的字串,然後和使用者名稱密碼一起提交到SSO Server.

  • SSO Server收到proxyA的請求之後校驗請求的合法性,然後獲取到proxyID-token-checksum檔案中的session_id.在驗證使用者密碼之後把登入資訊以proxyID—>username的形式寫到session中.之後返回使用者資訊給proxyA.

  • proxyA受到SSO Server的使用者資訊之後,判斷登入成功.

  • 當使用者在多個proxy上登入,就會在sesion中維護多個proxyID—>username的鍵值對資訊.使用者從一個系統退出,proxy代理client向SSO Server發起退出請求.SSO Server按照上面的步驟獲取到session id.刪除掉所有的session資料.這樣其他proxy的登入資訊也被刪除.下次訪問需重新登入.

實現

上面的流程都清楚了,下面來嘗試用程式碼實現

首先必然得有兩個模組,proxy跟SSO Server.為了能在機子上執行處效果,需要配置虛擬主機,如果想提前看到效果, 可以在這裡的SSO目錄下找到全部原始碼,在apache下配置好www.a.com, www.b.com以及www.sso.com三個網站,並且在/etc/hosts下配置www.sso.com的ip地址.然後把SSO目錄下的三個子資料夾分別放到對應目錄下.

整個系統中,proxy在三種情況下會請求SSO

  • attach.主要是為了通過client攜帶token給SSO.並且SSO 分配session_id 給client

  • login ,登入.proxy代理 client向SSO發起身份驗證的請求.

  • logout, 退出登入,清除SSO維護的session資料.

好了,整個SSO的分析就到這裡了,原始碼在github可以找到, 而且, 如果懂得上面的認證流程,看懂程式碼是很簡單的事情.

相關文章