前言
在企業(yè)業(yè)務(wù)發(fā)生變化時(shí),采用技術(shù)手段整合多個(gè)網(wǎng)站從來就不是一件輕松的事情绍傲。這里面的障礙主要有:
- 不同的賬號和權(quán)限管理體系
- 不同的界面風(fēng)格
- 生硬的鏈接跳轉(zhuǎn)
- 難以復(fù)用的后端接口
從我們的經(jīng)驗(yàn)看幻枉,以上問題要解決碰声,需要使用一種漸進(jìn)整合的架構(gòu),因?yàn)檎吓f內(nèi)容和生產(chǎn)新內(nèi)容是同時(shí)進(jìn)行的熬甫,我們做的事情就像是:開著飛機(jī)換引擎胰挑。
解決思路
本文介紹一種在跨域場景下實(shí)現(xiàn)單點(diǎn)登錄和網(wǎng)站內(nèi)容融合的方案,針對性解決前述 4 大障礙,其對應(yīng)的主要技術(shù)點(diǎn)是:
- 通過單點(diǎn)登錄統(tǒng)一賬號
- 通過 iframe 門戶頁提供統(tǒng)一的外框?qū)Ш讲藛?/li>
- 通過攔截鏈接跳轉(zhuǎn)和重置地址欄瞻颂,實(shí)現(xiàn)統(tǒng)一的鏈接跳轉(zhuǎn)行為
- 通過支持 CORS 請求協(xié)議豺谈,接口可以部署在任何一個(gè)關(guān)聯(lián)應(yīng)用
以下舉例說明此方案。
假設(shè)我們需要整合的網(wǎng)站分別是 a.my.com
(簡稱 a )和 b.my.com
(簡稱 b ) 贡这。
實(shí)現(xiàn)單點(diǎn)登錄
為了實(shí)現(xiàn)單點(diǎn)登錄茬末,我們需要新增單點(diǎn)登錄服務(wù)器 sso.my.com
。
改造 a盖矫、b 網(wǎng)站底層丽惭,讓他們的用戶賬號映射為一個(gè)統(tǒng)一的 uniqueId
。并在底層調(diào)用單點(diǎn)登錄服務(wù)器提供的登錄認(rèn)證辈双。
用戶首次訪問 a 網(wǎng)站感知到的流程如下:
- 用戶訪問頁面
//a.my.com/page.htm
-
a.my.com
調(diào)用sso.my.com
認(rèn)證服務(wù)進(jìn)行鑒權(quán) - 鑒權(quán)失敗责掏,跳轉(zhuǎn)登錄頁
//sso.my.com/login.htm?returnUrl=//a.my.com/page.htm
- 登錄后回跳進(jìn)入頁面
//a.my.com/page.htm
同理,用戶首次訪問 b 網(wǎng)站辐马,也會獲得一致的體驗(yàn)拷橘。
下面是一個(gè)淘寶的單點(diǎn)登錄的案例,訪問"我的淘寶"頁面喜爷,進(jìn)入的是登錄頁冗疮。
原頁面地址:https://i.taobao.com/my_taobao.htm
帶回跳的登錄頁地址:https://login.taobao.com/member/login.jhtml?redirect_url=https%3A%2F%2Fi.taobao.com%2Fmy_taobao.htm
(如圖 1 所示)
圖 1. 帶回跳的淘寶登錄頁地址
構(gòu)建 iframe 門戶頁
iframe 框架曾經(jīng)是早期網(wǎng)站內(nèi)容布局的一種方式,后來因?yàn)?Ajax 能自由地進(jìn)行局部刷新檩帐,iframe 的必要性下降术幔,加上開發(fā)維護(hù)的成本較高,逐漸被減少使用湃密,僅在部分場景使用诅挑。但本文正是充分利用 iframe 的特點(diǎn):能隔離外框?qū)Ш讲藛魏晚撁鎯?nèi)容。
為了實(shí)現(xiàn) iframe 門戶頁泛源,我們需要新增門戶服務(wù)器 portal.my.com
拔妥。
開發(fā)一個(gè) iframe 門戶頁 portal.my.com/home.htm
,以后所有的頁面請求达箍,都通過它嵌套訪問没龙。
改造 a、b 網(wǎng)站頁面的腳手架缎玫,讓頁面擁有兩種模式:獨(dú)立使用模式和嵌套使用模式硬纤,在嵌套使用模式下隱藏原有的菜單信息。
用戶訪問 a 網(wǎng)站感知到的流程如下:
- 用戶訪問頁面
//portal.my.com/home.htm?page=//a.my.com/page.htm
- iframe 門戶頁根據(jù)參數(shù)呈現(xiàn)外框?qū)Ш讲藛?/li>
- iframe 門戶頁中的 iframe赃磨,間接訪問
//a.my.com/page.htm?mode=nested
筝家,呈現(xiàn)頁面內(nèi)容。在這個(gè)例子中邻辉,模式 (mode
)參數(shù)值nested
表示頁面以嵌套的方式被訪問溪王,指示不要渲染頁面原有的菜單(如有)
同理腮鞍,用戶首次訪問 b 網(wǎng)站,也會獲得一致的體驗(yàn)在扰。
為了獲得更好的體驗(yàn)缕减,應(yīng)該更改原有頁面的主題,讓他們的主題顏色等樣式趨于一致芒珠,如圖 2 所示:
圖 2. 更改原有頁面的主題
攔截鏈接跳轉(zhuǎn)和重置地址欄
現(xiàn)在用戶直接通過頁面地址打開頁面桥狡,已經(jīng)能獲得我們所期望的頁面效果。但是頁面上的鏈接發(fā)生點(diǎn)擊時(shí)皱卓,會發(fā)生以下兩種情況:
- 在 iframe 中跳轉(zhuǎn)
- 在新窗口或新標(biāo)簽頁打開頁面
需要注意的是裹芝,頁面上原有的跳轉(zhuǎn)代碼目前還沒有去重構(gòu),所以跳轉(zhuǎn)地址仍然是直接地址娜汁,而不是形如 //portal.my.com/home.htm?page=//a.my.com/page.htm
這樣的封裝地址嫂易。 <a>
標(biāo)簽可以攔截點(diǎn)擊事件,代碼中的跳轉(zhuǎn)則只能重構(gòu)代碼掐禁,比如調(diào)用一個(gè)新的公共鏈接跳轉(zhuǎn)方法怜械。
我們可以制作一個(gè)公共的 JS 文件,并改造原有頁面傅事,引入這個(gè) JS 文件以便攔截跳轉(zhuǎn)缕允。在攔截到跳轉(zhuǎn)后:
- 針對情況 1,通過
history.pushState
重置地址欄蹭越,讓地址欄顯示新的封裝地址障本。 - 針對情況 2,在新窗口或新標(biāo)簽頁按照新的封裝地址打開頁面响鹃。
為了獲得更好的體驗(yàn)驾霜,我們還可以進(jìn)行 URL 重寫,讓用戶通過 REST 風(fēng)格的 URL 進(jìn)行訪問买置,這時(shí) URL 的格式是: //portal.my.com/home/page/a.my.com/page
粪糙。
圖 3. 攔截鏈接跳轉(zhuǎn)
改造后端接口
將后端接口改造為支持 CORS 請求的后端接口,這樣跨域訪問接口不再成為障礙忿项,接口可以部署到任意關(guān)聯(lián)應(yīng)用中猜旬。
圖 4 是 CORS 示意圖 (來源:https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)。與頁面不在一個(gè)域的請求倦卖,即為 CORS 請求。
圖 4. CORS 示意圖
對一個(gè)簡單的請求椿争,沒有自定義頭部怕膛,要么使用 GET
,要么使用 POST
秦踪,它的 Content-Type
請求頭是 text/plain
褐捻、multipart/form-data
或者 application/x-www-form-urlencoded
掸茅,瀏覽器會自動添加一個(gè)名叫 Origin
的額外的頭部發(fā)送 請求 。Origin
頭部包含請求頁面的協(xié)議域名柠逞、端口昧狮,這樣服務(wù)器可以很容易的決定它是否應(yīng)該提供響應(yīng)。服務(wù)器端對于 CORS 的支持板壮,主要就是通過設(shè)置 Access-Control-Allow-Origin
響應(yīng)頭來進(jìn)行的逗鸣。
瀏覽器發(fā)出的請求如清單 1 所示:
清單 1. 瀏覽器發(fā)出的請求
GET /api HTTP/1.1
…
HOST: api.my.com
Referer: http://portal.my.com/home.htm?page=//a.my.com/page.htm
Origin: http://portal.my.com
一個(gè)支持 CORS 的服務(wù)可能給出如清單 2 響應(yīng):
清單 2. 一個(gè)支持 CORS 的服務(wù)響應(yīng)
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://portal.my.com
Content-Type: application/json; charset=UTF8
…
[Payload Here]
通常,如果在使用 Fetch
發(fā)送請求時(shí)绰精,如果需要攜帶 cookie撒璧,還需指定 credentials 為 include
,如圖 5 所示
圖 5. 發(fā)送攜帶 Cookie 的跨域請求
注意:對于上傳文件等復(fù)雜請求(上傳請求監(jiān)聽了 XMLHttpRequestUpload
以便獲得上傳進(jìn)度)笨使,將觸發(fā)瀏覽器發(fā)送預(yù)檢請求的規(guī)則卿樱,這時(shí)需要在后端服務(wù)器正確響應(yīng) OPTIONS
請求。
總結(jié)
本方案在實(shí)現(xiàn)單點(diǎn)登錄硫椰、構(gòu)建 iframe 門戶頁繁调、攔截鏈接跳轉(zhuǎn)和重置地址欄、改造后端接口共 4 個(gè)方面靶草,漸進(jìn)地整合幾個(gè)關(guān)聯(lián)網(wǎng)站蹄胰,最終達(dá)到一致的用戶體驗(yàn),不僅是看上去一致爱致,而且為統(tǒng)一多個(gè)網(wǎng)站的底層架構(gòu)埋下伏筆烤送。