前后端分離實(shí)現(xiàn)SSO單點(diǎn)登錄

什么是SSO?

如果你點(diǎn)開這里,那么我默認(rèn)你已經(jīng)知道什么是SSO單點(diǎn)登錄。如果你還不知道那么可能本文對(duì)你也許沒多大幫助。

前言

為什么要寫前言?其實(shí)我很懶我不想寫文章、前言只是發(fā)個(gè)牢騷,很多文章都有寫到SSO單點(diǎn)登錄的實(shí)現(xiàn)說明,但大多都是介紹什么是session、什么是cookie之類的,千篇一律。文章很長(zhǎng)、但都略過了怎么在一個(gè)瀏覽器中不同域名下共享登錄狀態(tài)的核心操作,這很氣人!為了節(jié)省時(shí)間本篇僅講解如何共享的問題。

正文

一、假設(shè)我們有網(wǎng)站 A(www.a.com)和網(wǎng)站B(www.b.com)。在某一時(shí)刻用戶登錄了網(wǎng)站A,那么怎么通知用戶本地的網(wǎng)站B標(biāo)記用戶已經(jīng)登錄呢?也許有點(diǎn)繞,看代碼。我們首先需要在網(wǎng)站A的登錄成功頁(yè)面嵌入如下HTML代碼(這樣在我們登錄A成功的同時(shí)嵌入B):

<iframe src="http://www.b.com" style="height: 0px;width: 0px;display: none;"></iframe>

二、當(dāng)然這還沒有完成,嵌入了B的目的就是要網(wǎng)站B在該瀏覽器標(biāo)記用戶已經(jīng)登錄那么標(biāo)記登錄了?這個(gè)就需要我們從A的登錄結(jié)果中傳遞過去。在網(wǎng)站A的登錄成功頁(yè)面繼續(xù)嵌入如下javascript代碼(實(shí)現(xiàn)數(shù)據(jù)從A到iframe中B的傳遞)postMessage實(shí)例參考

<script>
var data=JSON.stringify({
        msg:'建議以字符串形式傳輸字符串',
        token:'這是從后臺(tái)而來的token'
})
document.getElementsByTagName('iframe')[0].contentWindow.postMessage(data,'*');
</script>

三、此時(shí)我們已經(jīng)通過postMessage方法傳輸數(shù)據(jù)到B,那么下一步我們就必須在B中接收該數(shù)據(jù)并存貯就好了(我們第一步中iframe中的src不一定非要指向B的首頁(yè),可以是B的某個(gè)頁(yè)面即可,以下所指的B頁(yè)面均指該src指向的頁(yè)面)。那么我們?cè)贐頁(yè)面下嵌入如下javascript代碼接收A的數(shù)據(jù)并存貯:

<script>
// 有消息從父級(jí)傳來時(shí) 存貯 tokenData
window.addEventListener('message',function(e){
    if(e.source!=window.parent) return;
    //e.data即是我們從A傳來的數(shù)據(jù) 這里使用localStorage進(jìn)行存貯(請(qǐng)勿使用sessionStorage)
    localStorage.setItem("tokenData",e.data);
},false);
</script>

四、其實(shí)到上面就完成了共享的過程,第三步就相當(dāng)于在網(wǎng)站B在用戶瀏覽器中存了一些數(shù)據(jù),當(dāng)用戶打開網(wǎng)站B時(shí)通過localStorage.getItem("tokenData")即可判斷用戶的是否登錄,當(dāng)然這個(gè)數(shù)據(jù)一般僅存貯用戶的一個(gè)標(biāo)識(shí)和過期時(shí)間,是否過期則交由服務(wù)器仔細(xì)判斷。

實(shí)例

以下提供完整的實(shí)例代碼進(jìn)行測(cè)試

  • a.html
<title>A首頁(yè)</title>
<meta charset="utf-8">

<p>
    <button onclick="login();" id="login">同步登錄</button>
    <p id="msg" style="display: none;">該用戶已經(jīng)登錄
        <button onclick="localStorage.clear();" id="login">注銷</button>
    </p>
</p>

<iframe src="http://www.b.com" style="height: 0px;width: 0px;display: none;"></iframe>

<script>
// 是否顯示“同步登錄”按鈕
var tokenData=localStorage.getItem('tokenData')
if(tokenData){
    document.getElementById('login').style.display='none';
    document.getElementById('msg').style.display='block';
}


// 同步登錄
function login(){
    let data=JSON.stringify({
        msg:'建議以字符串形式傳輸字符串',
        token:'這是從后臺(tái)而來的token'
    })
    document.getElementsByTagName('iframe')[0].contentWindow.postMessage(data,'*');
}



// 有消息從父級(jí)傳來時(shí) 存貯 tokenData
window.addEventListener('message',function(e){
    if(e.source!=window.parent) return;
    localStorage.setItem("tokenData",e.data);
},false);

</script>


  • b.html
<title>B首頁(yè)</title>
<meta charset="utf-8">

<p>
    <button onclick="login();" id="login">同步登錄</button>
    <p id="msg" style="display: none;">該用戶已經(jīng)登錄
        <button onclick="localStorage.clear();" id="login">注銷</button>
    </p>
</p>

<iframe src="http://www.a.com" style="height: 0px;width: 0px;display: none;"></iframe>

<script>
// 是否顯示“同步登錄”按鈕
var tokenData=localStorage.getItem('tokenData')
if(tokenData){
    document.getElementById('login').style.display='none';
    document.getElementById('msg').style.display='block';
}


// 同步登錄
function login(){
    let data=JSON.stringify({
        msg:'建議以字符串形式傳輸字符串',
        token:'這是從后臺(tái)而來的token'
    })
    document.getElementsByTagName('iframe')[0].contentWindow.postMessage(data,'*');
}



// 有消息從父級(jí)傳來時(shí) 存貯 tokenData
window.addEventListener('message',function(e){
    if(e.source!=window.parent) return;
    localStorage.setItem("tokenData",e.data);
},false);

</script>


效果(注:A、B域名均在本地模擬)

  • 未登錄之前A、B的效果均如下圖:
未登錄
  • 點(diǎn)擊A “同步登錄”按鈕 然后打開B www.b.com 如果你已經(jīng)打開,那么請(qǐng)刷新B即可看到如下效果:
同步登錄效果
  • 例子很簡(jiǎn)潔由于一些細(xì)節(jié)并未過于深究,比如點(diǎn)擊A的“同步登錄”按鈕,A的狀態(tài)并未改變等,感興趣可以自行完善。

結(jié)語

也許你可能會(huì)覺得文章與標(biāo)題不符,因?yàn)檫@里并未講到后端服務(wù)器也未曾進(jìn)行登錄,僅僅實(shí)現(xiàn)的跨域同步信息。那么我就再啰嗦一下,跨域同步信息就是SSO實(shí)現(xiàn)的核心,無論是否前后端分離,你最終都會(huì)獲取到一個(gè)token或者sessionid之類的,共享這個(gè)數(shù)據(jù)就能實(shí)現(xiàn)效果。這里的A頁(yè)面和B頁(yè)面不僅僅指A、B的首頁(yè)而是指某一個(gè)可直接訪問的頁(yè)面即可。關(guān)于安全的問題,我們說了token類似:{id:1,time:1538884342},SSO服務(wù)器需要驗(yàn)證該token是否未被篡改、是否過期,id 為1的用戶是否登錄。略!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,545評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,983評(píng)論 25 709
  • 【釋義】粉、墨:搽臉和畫眉用的化妝品。原指演員化妝上臺(tái)演戲。比喻壞人經(jīng)過一番打扮,登上政治舞臺(tái)。 如此可謂貶義 近...
    嗖嘎淺淺閱讀 340評(píng)論 0 2

友情鏈接更多精彩內(nèi)容