更新時間:2022-12-20 14:33:45 來源:動力節(jié)點 瀏覽1561次
單點登錄多處都能夠?qū)嵱茫诓煌膽铆h(huán)境下,可以采用不同的單點登錄實現(xiàn)方案來滿足需求,為此掌握這類相關的知識點也能夠?qū)ξ覀內(nèi)蘸蟮拿嬖噹砗艽蟮膸椭?/p>
一、共享Session
共享Session可謂是實現(xiàn)單點登錄最直接、最簡單的方式。將用戶認證信息保存于Session中,即以Session內(nèi)存儲的值為用戶憑證,這在單個站點內(nèi)使用是很正常也很容易實現(xiàn)的,而在用戶驗證、用戶信息管理與業(yè)務應用分離的場景下即會遇到單點登錄的問題,在應用體系簡單,子系統(tǒng)很少的情況下,可以考慮采用Session共享的方法來處理這個問題。
這個架構(gòu)我使用了基于Redis的Session共享方案。將Session存儲于Redis上,然后將整個系統(tǒng)的全局Cookie Domain設置于頂級域名上,這樣SessionID就能在各個子系統(tǒng)間共享。分布式Session共享解決方案,這篇推薦大家看下。
這個方案存在著嚴重的擴展性問題,首先,ASP.NET的Session存儲必須為SessionStateItemCollection對象,而存儲的結(jié)構(gòu)是經(jīng)過序列化后經(jīng)過加密存儲的。
并且當用戶訪問應用時,他首先做的就是將存儲容器里的所有內(nèi)容全部取出,并且反序列化為SessionStateItemCollection對象。
這就決定了他具有以下約束:
1.Session中所涉及的類型必須是子系統(tǒng)中共同擁有的(即程序集、類型都需要一致),這導致Session的使用受到諸多限制;
2.跨頂級域名的情況完全無法處理;
二、基于OpenId的單點登錄
這種單點登錄將用戶的身份標識信息簡化為OpenId存放于客戶端,當用戶登錄某個子系統(tǒng)時,將OpenId傳送到服務端,服務端根據(jù)OpenId構(gòu)造用戶驗證信息,多用于C/S與B/S相結(jié)合的系統(tǒng),流程如下:
由上圖可以看到,這套單點登錄依賴于OpenId的傳遞,其驗證的基礎在于OpenId的存儲以及發(fā)送。
1.當用戶第一次登錄時,將用戶名密碼發(fā)送給驗證服務;
2.驗證服務將用戶標識OpenId返回到客戶端;
3.客戶端進行存儲;
4.訪問子系統(tǒng)時,將OpenId發(fā)送到子系統(tǒng);
5.子系統(tǒng)將OpenId轉(zhuǎn)發(fā)到驗證服務;
6.驗證服務將用戶認證信息返回給子系統(tǒng);
7.子系統(tǒng)構(gòu)建用戶驗證信息后將授權(quán)后的內(nèi)容返回給客戶端。
這套單點登錄驗證機制的主要問題在于他基于C/S架構(gòu)下將用戶的OpenId存儲于客戶端,在子系統(tǒng)之間發(fā)送OpenId,而B/S模式下要做到這一點就顯得較為困難。為了處理這個問題我們將引出下一種方式,這種方式將解決B/S模式下的OpenId的存儲、傳遞問題。
三、基于Cookie的OpenId存儲方案
我們知道,Cookie的作用在于充當一個信息載體在Server端和Browser端進行信息傳遞,而Cookie一般是以域名為分割的,例如a.xxx.com與b.xxx.com的Cookie是不能互相訪問的,但是子域名是可以訪問上級域名的Cookie的。即a.xxx.com和b.xxx.com是可以訪問xxx.com下的Cookie的,于是就能將頂級域名的Cookie作為OpenId的載體。
驗證步驟和上第二個方法非常相似:
1、 在提供驗證服務的站點里登錄;
2、 將OpenId寫入頂級域名Cookie里;
3、 訪問子系統(tǒng)(Cookie里帶有OpenId)
4、 子系統(tǒng)取出OpenId通過并向驗證服務發(fā)送OpenId
5、 返回用戶認證信息
6、 返回授權(quán)后的內(nèi)容
在以上兩種方法中我們都可以看到通過OpenId解耦了Session共享方案中的類型等問題,并且構(gòu)造用戶驗證信息將更靈活,子系統(tǒng)間的驗證是相互獨立的,但是在第三種方案里,我們基于所有子系統(tǒng)都是同一個頂級域名的假設,而在實際生產(chǎn)環(huán)境里有多個域名是很正常的事情,那么就不得不考慮跨域問題究竟如何解決。
四、B/S多域名環(huán)境下的單點登錄處理
在多個頂級域名的情況下,我們將無法讓各個子系統(tǒng)的OpenId共享。處理B/S環(huán)境下的跨域問題,我們首先就應該想到JSONP的方案。
驗證步驟如下:
1、 用戶通過登錄子系統(tǒng)進行用戶登錄;
2、 用戶登錄子系統(tǒng)記錄了用戶的登錄狀態(tài)、OpenId等信息;
3、 用戶使用業(yè)務子系統(tǒng);
4、 若用戶未登錄業(yè)務子系統(tǒng)則將用戶跳轉(zhuǎn)至用戶登錄子系統(tǒng);
5、 用戶子系統(tǒng)通過JSONP接口將用戶OpenId傳給業(yè)務子系統(tǒng);
6、 業(yè)務子系統(tǒng)通過OpenId調(diào)用驗證服務;
7、 驗證服務返回認證信息、業(yè)務子系統(tǒng)構(gòu)造用戶登錄憑證;(此時用戶客戶端已經(jīng)與子業(yè)務系統(tǒng)的驗證信息已經(jīng)一一對應)
8、 將用戶登錄結(jié)果返回用戶登錄子系統(tǒng),若成功登錄則將用戶跳轉(zhuǎn)回業(yè)務子系統(tǒng);
9、 將授權(quán)后的內(nèi)容返回客戶端;
五、安全問題
經(jīng)過以上步驟,跨域情況下的單點登錄問題已經(jīng)可以得到解決。而在整個開發(fā)過程初期,我們采用用戶表中紀錄一個OpenId字段來保存用戶OpenId,而這個機制下很明顯存在一些安全性、擴展性問題。這個擴展性問題主要體現(xiàn)在一個方面:OpenId的安全性和用戶體驗的矛盾。
整個單點登錄的機制決定了OpenId是會出現(xiàn)在客戶端的,所以OpenId需要有過期機制,假如用戶在一個終端登錄的話可以選擇在用戶每次登錄或者每次退出時刷新OpenId,而在多終端登錄的情況下就會出現(xiàn)矛盾:當一個終端刷新了OpenId之后其他終端將無法正常授權(quán)。
而最終,我采用了單用戶多OpenId的解決方案。每次用戶通過用戶名/密碼登錄時,產(chǎn)生一個OpenId保存在Redis里,并且設定過期時間,這樣多個終端登錄就會有多個OpenId與之對應,不再會存在一個OpenId失效所有終端驗證都失效的情況。
以上就是“互聯(lián)網(wǎng)高頻出現(xiàn)的單點登錄面試題”,你能回答上來嗎?如果想要了解更多的Java面試題相關內(nèi)容,可以關注動力節(jié)點Java官網(wǎng)。