單點(diǎn)登錄的 3 種實(shí)現(xiàn)方式
前言
實(shí)現(xiàn)方式一:父域 Cookie
實(shí)現(xiàn)方式二:認(rèn)證中心
實(shí)現(xiàn)方式三:LocalStorage 跨域
補(bǔ)充:域名分級(jí)
前言
實(shí)現(xiàn)方式一:父域 Cookie
在將具體實(shí)現(xiàn)之前,我們先來(lái)聊一聊 Cookie 的作用域。
Cookie 的作用域由 domain 屬性和 path 屬性共同決定。domain 屬性的有效值為當(dāng)前域或其父域的域名/IP地址,在 Tomcat 中,domain 屬性默認(rèn)為當(dāng)前域的域名/IP地址。path 屬性的有效值是以“/”開(kāi)頭的路徑,在 Tomcat 中,path 屬性默認(rèn)為當(dāng)前 Web 應(yīng)用的上下文路徑。
如果將 Cookie 的 domain 屬性設(shè)置為當(dāng)前域的父域,那么就認(rèn)為它是父域 Cookie。Cookie 有一個(gè)特點(diǎn),即父域中的 Cookie 被子域所共享,換言之,子域會(huì)自動(dòng)繼承父域中的Cookie。
利用 Cookie 的這個(gè)特點(diǎn),不難想到,將 Session ID(或 Token)保存到父域中不就行了。沒(méi)錯(cuò),我們只需要將 Cookie 的 domain 屬性設(shè)置為父域的域名(主域名),同時(shí)將 Cookie 的 path 屬性設(shè)置為根路徑,這樣所有的子域應(yīng)用就都可以訪問(wèn)到這個(gè) Cookie 了。
不過(guò)這要求應(yīng)用系統(tǒng)的域名需建立在一個(gè)共同的主域名之下,如 tieba.baidu.com 和 map.baidu.com,它們都建立在 baidu.com 這個(gè)主域名之下,那么它們就可以通過(guò)這種方式來(lái)實(shí)現(xiàn)單點(diǎn)登錄。
總結(jié):此種實(shí)現(xiàn)方式比較簡(jiǎn)單,但不支持跨主域名。
實(shí)現(xiàn)方式二:認(rèn)證中心
我們可以部署一個(gè)認(rèn)證中心,認(rèn)證中心就是一個(gè)專門(mén)負(fù)責(zé)處理登錄請(qǐng)求的獨(dú)立的 Web 服務(wù)。
用戶統(tǒng)一在認(rèn)證中心進(jìn)行登錄,登錄成功后,認(rèn)證中心記錄用戶的登錄狀態(tài),并將 Token 寫(xiě)入 Cookie。(注意這個(gè) Cookie 是認(rèn)證中心的,應(yīng)用系統(tǒng)是訪問(wèn)不到的。)
應(yīng)用系統(tǒng)檢查當(dāng)前請(qǐng)求有沒(méi)有 Token,如果沒(méi)有,說(shuō)明用戶在當(dāng)前系統(tǒng)中尚未登錄,那么就將頁(yè)面跳轉(zhuǎn)至認(rèn)證中心。由于這個(gè)操作會(huì)將認(rèn)證中心的 Cookie 自動(dòng)帶過(guò)去,因此,認(rèn)證中心能夠根據(jù) Cookie 知道用戶是否已經(jīng)登錄過(guò)了。
如果認(rèn)證中心發(fā)現(xiàn)用戶尚未登錄,則返回登錄頁(yè)面,等待用戶登錄,如果發(fā)現(xiàn)用戶已經(jīng)登錄過(guò)了,就不會(huì)讓用戶再次登錄了,而是會(huì)跳轉(zhuǎn)回目標(biāo) URL ,并在跳轉(zhuǎn)前生成一個(gè) Token,拼接在目標(biāo) URL 的后面,回傳給目標(biāo)應(yīng)用系統(tǒng)。
應(yīng)用系統(tǒng)拿到 Token 之后,還需要向認(rèn)證中心確認(rèn)下 Token 的合法性,防止用戶偽造。確認(rèn)無(wú)誤后,應(yīng)用系統(tǒng)記錄用戶的登錄狀態(tài),并將 Token 寫(xiě)入 Cookie,然后給本次訪問(wèn)放行。(注意這個(gè) Cookie 是當(dāng)前應(yīng)用系統(tǒng)的,其他應(yīng)用系統(tǒng)是訪問(wèn)不到的。)當(dāng)用戶再次訪問(wèn)當(dāng)前應(yīng)用系統(tǒng)時(shí),就會(huì)自動(dòng)帶上這個(gè) Token,應(yīng)用系統(tǒng)驗(yàn)證 Token 發(fā)現(xiàn)用戶已登錄,于是就不會(huì)有認(rèn)證中心什么事了。
這里順便介紹兩款認(rèn)證中心的開(kāi)源實(shí)現(xiàn):
Apereo CAS 是一個(gè)企業(yè)級(jí)單點(diǎn)登錄系統(tǒng),其中 CAS 的意思是”Central Authentication Service“。它最初是耶魯大學(xué)實(shí)驗(yàn)室的項(xiàng)目,后來(lái)轉(zhuǎn)讓給了 JASIG 組織,項(xiàng)目更名為 JASIG CAS,后來(lái)該組織并入了Apereo 基金會(huì),項(xiàng)目也隨之更名為 Apereo CAS。
XXL-SSO 是一個(gè)簡(jiǎn)易的單點(diǎn)登錄系統(tǒng),由大眾點(diǎn)評(píng)工程師許雪里個(gè)人開(kāi)發(fā),代碼比較簡(jiǎn)單,沒(méi)有做安全控制,因而不推薦直接應(yīng)用在項(xiàng)目中,這里列出來(lái)僅供參考。
總結(jié):此種實(shí)現(xiàn)方式相對(duì)復(fù)雜,支持跨域,擴(kuò)展性好,是單點(diǎn)登錄的標(biāo)準(zhǔn)做法。
實(shí)現(xiàn)方式三:LocalStorage 跨域
前面,我們說(shuō)實(shí)現(xiàn)單點(diǎn)登錄的關(guān)鍵在于,如何讓 Session ID(或 Token)在多個(gè)域中共享。
父域 Cookie 確實(shí)是一種不錯(cuò)的解決方案,但是不支持跨域。那么有沒(méi)有什么奇淫技巧能夠讓 Cookie 跨域傳遞呢?
很遺憾,瀏覽器對(duì) Cookie 的跨域限制越來(lái)越嚴(yán)格。Chrome 瀏覽器還給 Cookie 新增了一個(gè) SameSite 屬性,此舉幾乎禁止了一切跨域請(qǐng)求的 Cookie 傳遞(超鏈接除外),并且只有當(dāng)使用 HTTPs 協(xié)議時(shí),才有可能被允許在 AJAX 跨域請(qǐng)求中接受服務(wù)器傳來(lái)的 Cookie。
不過(guò),在前后端分離的情況下,完全可以不使用 Cookie,我們可以選擇將 Session ID (或 Token )保存到瀏覽器的 LocalStorage 中,讓前端在每次向后端發(fā)送請(qǐng)求時(shí),主動(dòng)將 LocalStorage 的數(shù)據(jù)傳遞給服務(wù)端。這些都是由前端來(lái)控制的,后端需要做的僅僅是在用戶登錄成功后,將 Session ID (或 Token )放在響應(yīng)體中傳遞給前端。
在這樣的場(chǎng)景下,單點(diǎn)登錄完全可以在前端實(shí)現(xiàn)。前端拿到 Session ID (或 Token )后,除了將它寫(xiě)入自己的 LocalStorage 中之外,還可以通過(guò)特殊手段將它寫(xiě)入多個(gè)其他域下的 LocalStorage 中。
關(guān)鍵代碼如下:
//?獲取?token
var?token?=?result.data.token;
//?動(dòng)態(tài)創(chuàng)建一個(gè)不可見(jiàn)的iframe,在iframe中加載一個(gè)跨域HTML
var?iframe?=?document.createElement("iframe");
iframe.src?=?"http://app1.com/localstorage.html";
document.body.append(iframe);
//?使用postMessage()方法將token傳遞給iframe
setTimeout(function?()?{
????iframe.contentWindow.postMessage(token,?"http://app1.com");
},?4000);
setTimeout(function?()?{
????iframe.remove();
},?6000);
//?在這個(gè)iframe所加載的HTML中綁定一個(gè)事件監(jiān)聽(tīng)器,當(dāng)事件被觸發(fā)時(shí),把接收到的token數(shù)據(jù)寫(xiě)入localStorage
window.addEventListener('message',?function?(event)?{
????localStorage.setItem('token',?event.data)
},?false);
前端通過(guò) iframe+postMessage() 方式,將同一份 Token 寫(xiě)入到了多個(gè)域下的 LocalStorage 中,前端每次在向后端發(fā)送請(qǐng)求之前,都會(huì)主動(dòng)從 LocalStorage 中讀取 Token 并在請(qǐng)求中攜帶,這樣就實(shí)現(xiàn)了同一份 Token 被多個(gè)域所共享。
總結(jié):此種實(shí)現(xiàn)方式完全由前端控制,幾乎不需要后端參與,同樣支持跨域。
補(bǔ)充:域名分級(jí)
從專業(yè)的角度來(lái)說(shuō)(根據(jù)《計(jì)算機(jī)網(wǎng)絡(luò)》中的定義),.com、.cn 為一級(jí)域名(也稱頂級(jí)域名),.com.cn、baidu.com 為二級(jí)域名,sina.com.cn、tieba.baidu.com 為三級(jí)域名,以此類推,N 級(jí)域名就是 N-1 級(jí)域名的直接子域名。
從使用者的角度來(lái)說(shuō),一般把可支持獨(dú)立備案的主域名稱作一級(jí)域名,如 baidu.com、sina.com.cn 皆可稱作一級(jí)域名,在主域名下建立的直接子域名稱作二級(jí)域名,如 tieba.baidu.com 為二級(jí)域名。
為了避免歧義,本人將使用“主域名“替代”一級(jí)域名“的說(shuō)法。
逆鋒起筆是一個(gè)專注于程序員圈子的技術(shù)平臺(tái),你可以收獲最新技術(shù)動(dòng)態(tài)、最新內(nèi)測(cè)資格、BAT等大廠的經(jīng)驗(yàn)、精品學(xué)習(xí)資料、職業(yè)路線、副業(yè)思維,微信搜索逆鋒起筆關(guān)注!
推薦↓↓↓ ?編程開(kāi)發(fā)
