浅谈电商网站开发中用户会话管理机制的设计和实现原理
筆者由于工作需要,最近對(duì)國內(nèi)外兩款知名的電商網(wǎng)站的用戶會(huì)話管理(User Session Management) 的實(shí)現(xiàn)機(jī)制做了一些調(diào)研,這里把我學(xué)習(xí)到的一些知識(shí)分享給各位同行,希望起到拋磚引玉的作用。
我們首先看看大家日常生活中都會(huì)使用的某寶網(wǎng)站的用戶會(huì)話管理機(jī)制。
在電腦端訪問某寶網(wǎng),輸入用戶名和密碼,點(diǎn)擊登錄:
會(huì)觀察到一個(gè) HTTP Post 請(qǐng)求,login,發(fā)送往后臺(tái)服務(wù)器:
https://login.taobao.com/newlogin/login.do?appName=taobao&fromSite=0
該請(qǐng)求的 Form Data 中包含 loginId 和 password2 兩個(gè)字段,分別維護(hù)了用戶輸入的用戶名的明文,以及密碼進(jìn)行 RSA 加密后的值。
下面介紹如何自行找到前端將用戶輸入的登錄密碼進(jìn)行 RSA 加密的準(zhǔn)確位置。
在 Chrome 開發(fā)者工具里找到 login 請(qǐng)求,在 Initiator 面板里找到發(fā)起該請(qǐng)求的調(diào)用棧。稍有經(jīng)驗(yàn)的前端開發(fā)人員,從 onClick 和 t.loginSubmit 就能推斷出,用戶名和密碼的輸入框,實(shí)現(xiàn)在一個(gè) Form 表單里,點(diǎn)擊登錄按鈕后,觸發(fā)表單的 Submit.
打開上圖找到的 index.js 文件:
https://x.alicdn.com/vip/havana-nlogin/0.5.61/index.js
直接搜索關(guān)鍵字 password2,很快就能找到 RSA 加密的代碼位置:
設(shè)置斷點(diǎn)后,運(yùn)行時(shí)點(diǎn)擊登錄按鈕,斷點(diǎn)觸發(fā),可以進(jìn)入 rsaPassword 函數(shù),查看 RSA 加密算法的明細(xì)。
這個(gè) index.js 里還能發(fā)現(xiàn)一些有趣的東西。比如提供了 rsaPassword 方法的模塊內(nèi)部,還維護(hù)了一個(gè)支持的國家列表,countryList,里面有 168 個(gè)國家和地區(qū):
但是在瀏覽器端打開某寶網(wǎng),國家和地區(qū)的下拉列表里,只能看到十余條記錄。這應(yīng)該是前臺(tái)某處根據(jù)某種邏輯做了過濾:
此外,我們?cè)谶@個(gè)電商網(wǎng)站首頁右邊區(qū)域,能看到快速充值話費(fèi)的面板,如下圖綠色高亮區(qū)域所示:
該頁面的 HTML 源代碼,并不是靜態(tài)編寫于首頁的 HTML 文件中,而是通過一個(gè)叫做 bianming-phone("便民"的拼音加上"手機(jī)"的英文單詞 phone) 的 HTTP 請(qǐng)求,從后臺(tái)讀取到前臺(tái),然后再注入到前臺(tái)頁面中:
同理,還有這個(gè)旅行視圖片段(相當(dāng)于 SAP UI5 里的 View Fragment):
讀取該視圖片段的 HTTP 請(qǐng)求:bianming-trip
看到這里,筆者不由得聯(lián)想起 SAP Commerce Cloud 前臺(tái)的 CMS 驅(qū)動(dòng)設(shè)計(jì),二者都是從電商頁面連接的后臺(tái)系統(tǒng)讀取部分頁面配置信息,可謂異曲同工。
- SAP S/4HANA 的 UI 風(fēng)格是 Fiori UX,實(shí)現(xiàn)的前端框架是 SAP UI5;
- SAP Commerce Cloud UI 實(shí)現(xiàn)的前端框架是 Angular;
- help.sap.com 采用 AngulaJS 實(shí)現(xiàn),www.sap.com 使用的是 React.
- 某寶網(wǎng)首頁,采用的是阿里自研的前端框架,Kissy:
我們?cè)谀硨毦W(wǎng)首頁看到琳瑯滿目的商品圖片,都是被 Kissy 驅(qū)動(dòng),通過向 CDN 服務(wù)器發(fā)起的數(shù)據(jù)請(qǐng)求而被加載的:
在這些頁面片段的源代碼里還看到一些有意思的內(nèi)容,比如這種“上線請(qǐng)刪除”的注釋。我現(xiàn)在瀏覽的就是上線后的代碼呀,咋還能夠看到這些注釋 😃
我們?cè)陔娚叹W(wǎng)站上購物時(shí),選擇好了自己心儀的商品,加入購物車之后,當(dāng)然不希望點(diǎn)擊結(jié)帳之后,忽然彈出要求重新登錄的界面,這豈不是令人掃興。另外,當(dāng)我們不小心誤操作,點(diǎn)擊了瀏覽器刷新按鈕,我們期望頁面刷新后,仍然處于登錄狀態(tài),之前添加到購物車?yán)锏纳唐凡粫?huì)丟失。這些都屬于用戶會(huì)話管理的范疇。
某寶網(wǎng)頁面的用戶會(huì)話管理,是通過客戶端 Cookie 和服務(wù)器端維護(hù)的用戶會(huì)話對(duì)象來實(shí)現(xiàn)的。
用戶成功登錄之后,服務(wù)器創(chuàng)建對(duì)應(yīng)的 Session 對(duì)象,返回給 login HTTP 請(qǐng)求的響應(yīng)頭部,包含了很多 set-cookie 字段:
瀏覽器解析這些響應(yīng),將服務(wù)器頒發(fā)的 Cookie 設(shè)置到本地。下一次用戶再次操作電商網(wǎng)站,觸發(fā)新的發(fā)向服務(wù)器端的請(qǐng)求時(shí),瀏覽器會(huì)自動(dòng)將這些 Cookie 字段設(shè)置到請(qǐng)求頭部。服務(wù)器接收到這些 Cookie 字段,就能在內(nèi)存中定位到之前該用戶登錄后對(duì)應(yīng)創(chuàng)建的 Session 對(duì)象,從而能夠識(shí)別出該用戶。
這些 Cookie 在 Chrome 開發(fā)者工具里也能查看。
- Cookie 的 Expires 字段存放的是過期時(shí)間,當(dāng) Expires 為 Session 時(shí),意為該 Cookie 只在當(dāng)前會(huì)話內(nèi)有效,瀏覽器關(guān)閉則 Cookie 自動(dòng)刪除。
- 帶有 Http Only 的 Cookie,無法被客戶端 JavaScript 代碼讀取,提高了安全性,避免了 Cookie 通過 XSS 攻擊被竊取的可能。
- Secure 為 true 的 Cookie,無法通過 HTTP 協(xié)議傳輸?shù)椒?wù)器,只能通過 HTTPS 發(fā)送。
這個(gè)電商網(wǎng)站服務(wù)器頒發(fā)的 Cookie 里,字段 lid 存儲(chǔ)的是用戶名經(jīng)過 URL encode 之后的值,dnk 存放的是用戶名的 Unicode:
下面再看另一款來自國外的電商網(wǎng)站,SAP Commerce Cloud 的用戶會(huì)話管理機(jī)制的設(shè)計(jì)和實(shí)現(xiàn)。
SAP Commerce Cloud UI,基于 100% API 驅(qū)動(dòng)的無頭電商架構(gòu),Commerce 后臺(tái)將 Commerce 核心業(yè)務(wù)通過 OCC(Omni Commerce Connect) API 的方式暴露出來。借助這些 API 和開源的 SAP Spartacus 庫,客戶可以自行開發(fā)具備個(gè)性化 UX 的電商網(wǎng)站。
SAP Commerce Cloud 有個(gè)名為 Oauth2 的 extension,基于 OAuth 2.0 協(xié)議實(shí)現(xiàn)了用戶認(rèn)證和令牌頒發(fā)/功能,支持 OAuth 2.0 協(xié)議定義的包括 Resource Owner Password Flow 在內(nèi)的全部四種認(rèn)證流。
SAP Commerce Cloud UI 扮演了 OAuth 2.0 認(rèn)證框架中的客戶端 (Client) 角色,通過消費(fèi) SAP Commerce Oauth2 擴(kuò)展提供的 OAuth 系列 API,實(shí)現(xiàn)用戶會(huì)話管理。
讓我們從最初始的用戶登錄場景說起。
輸入用戶名和密碼:
SAP Commerce Cloud UI 調(diào)用 Commerce OAuth2 API,endpoint 為 /authorizationserver/oauth/token, 將用戶名,密碼,client_id 和 client_secret 去換取訪問令牌(Access Token)和刷新令牌(Refresh Token).
這里的 SAP Commerce Cloud UI 作為 OAuth 認(rèn)證體系里的客戶端,其 client_id 和 client_secret 在 Commerce Backoffice 里配置:
服務(wù)器端驗(yàn)證通過后,會(huì)頒發(fā)訪問令牌和刷新令牌,如下圖 access_token 和 refresh_token 字段所示:
SAP Commerce Cloud UI 在 OAuth 體系中扮演的角色是客戶端,通過訪問令牌,獲得訪問 Commerce 后臺(tái)服務(wù)器上的業(yè)務(wù)數(shù)據(jù)的許可。而刷新令牌,用于當(dāng)訪問令牌過期時(shí),由客戶端憑借其換取新的訪問令牌。刷新令牌本身是一個(gè)憑證,表明持有其的客戶端,曾經(jīng)通過 OAuth 認(rèn)證,獲得了訪問受保護(hù)資源的許可,當(dāng)通過刷新令牌再次請(qǐng)求新的訪問令牌時(shí),客戶端不用再從頭開始走一遍 OAuth 認(rèn)證的完整流程。
SAP Commerce Cloud 的訪問令牌和刷新令牌都有過期時(shí)間,有時(shí)也稱為 TTL(Time-to-Live,存活時(shí)間),默認(rèn)值分別為12小時(shí)和30天。
而我們團(tuán)隊(duì)的開發(fā)人員,在開發(fā) SAP Commerce Cloud UI 用戶會(huì)話管理功能,進(jìn)行各種邊界條件的測(cè)試時(shí),為了方便起見,通常將自己本地搭建的 Commerce 服務(wù)器上令牌的過期時(shí)間進(jìn)行了調(diào)整。比如下圖的例子,二者分別調(diào)整為30秒和60秒之后過期:
訪問令牌獲取之后,在接下來 Commerce Cloud UI 消費(fèi)后臺(tái) OCC API 時(shí),會(huì)將其附加在 HTTP 請(qǐng)求的頭部字段里:
如果此時(shí)訪問令牌已經(jīng)過期,則該請(qǐng)求會(huì)收到服務(wù)器 401 錯(cuò)誤的應(yīng)答:
以及錯(cuò)誤詳情 InvalidTokenError:Access token expired: IqQ-8cYzHV1gjQOpnYytHTFPt30
顯然這種偏技術(shù)的錯(cuò)誤消息不應(yīng)該顯示給用戶,幸運(yùn)的是我們還有刷新令牌。此時(shí),SAP Commerce Cloud UI 會(huì)將過期的訪問令牌,連同刷新令牌一齊發(fā)送給 Commerce 后臺(tái),申請(qǐng)一個(gè)新的訪問令牌:
SAP Commerce Cloud UI 初次登錄申請(qǐng)令牌時(shí),grant_type 的值為 password;而訪問令牌過期,使用刷新令牌重新申請(qǐng)時(shí),grant_type 的值應(yīng)該填充為 refresh_token.
如果刷新令牌的過期時(shí)間也到達(dá)了,該怎么辦?沒有刷新令牌,也就無從獲取新的訪問令牌。因此,我們會(huì)將用戶重定向到登錄頁面,顯示一條“Session expired”的提示信息,讓其登錄之后,重新獲取訪問令牌和刷新令牌。
本文前一章節(jié),從某寶首頁登錄說起曾經(jīng)提到,我們?cè)陔娚叹W(wǎng)站上購物,如果不小心刷新了瀏覽器,只要客戶端存儲(chǔ)的 Cookie 尚未過期,就可仍然保持登錄狀態(tài)。這樣,客戶刷新之前的會(huì)話,比如添加商品到購物車,或者正在進(jìn)行結(jié)帳的某一步,仍然處于有效狀態(tài)。
SAP Commerce Cloud UI 通過將訪問令牌持久化到瀏覽器的 Local Storage 中來實(shí)現(xiàn)上述場景。
每當(dāng)用戶成功登錄后,我們將 Commerce 后臺(tái)服務(wù)器頒發(fā)的訪問令牌進(jìn)行持久化存儲(chǔ),保存到瀏覽器 Local Storage 中。
每次 SAP Commerce Cloud UI 初始化時(shí),通過 Angular APP_INITIALIZER 這個(gè)注入令牌,我們開發(fā)了 AuthStatePersistenceService 服務(wù),將瀏覽器本地存儲(chǔ)中的令牌同步到內(nèi)存中。
采取這種設(shè)計(jì)后,即使用戶在購物過程中刷新了瀏覽器,SAP Commerce Cloud UI 重新加載后,從 Local Storage 中取出訪問令牌同步到內(nèi)存中,接下來的用戶操作,繼續(xù)使用該令牌調(diào)用 Commerce OCC API,不會(huì)因?yàn)g覽器刷新而中斷。
總結(jié)起來,SAP Commerce Cloud UI 有關(guān)訪問令牌和刷新令牌的使用場景如下:
(1) 用戶登錄后,SAP Commerce Cloud UI 將服務(wù)器頒發(fā)的訪問令牌存儲(chǔ)于內(nèi)存中,并持久化到瀏覽器 Local Storage.
對(duì)于刷新令牌,出于安全性考慮,我們團(tuán)隊(duì)實(shí)現(xiàn)時(shí),僅將其維護(hù)在應(yīng)用內(nèi)存中,并不進(jìn)行持久化操作。
(2) 當(dāng)用戶操作 UI,觸發(fā) API 調(diào)用后收到服務(wù)器返回的訪問令牌過期的錯(cuò)誤之后,SAP Commerce Cloud UI 自動(dòng)利用刷新令牌,申請(qǐng)新的訪問令牌;待拿到新的訪問令牌之后,使用該令牌重新調(diào)用之前因?yàn)榕f的訪問令牌過期而失敗的 API;這一系列機(jī)制對(duì)于用戶來說完全透明,用戶在界面上的操作不會(huì)受到任何影響。
(3) 如果用戶操作觸發(fā)的 API 調(diào)用收到的服務(wù)器返回為刷新令牌過期,SAP Commerce Cloud UI 會(huì)暫存當(dāng)前用戶瀏覽頁面的 URL,并將用戶重定向到登錄頁面;用戶重新登錄后,獲取到新的訪問令牌和刷新令牌,再被 SAP Commerce Cloud 重定向到刷新令牌過期時(shí)正在操作的頁面。
總結(jié)
本文選擇了國內(nèi)外兩款最具代表性的電商購物網(wǎng)站,使用 Chrome 開發(fā)者工具進(jìn)行探究,分析了這兩款電商網(wǎng)站用戶會(huì)話管理機(jī)制的設(shè)計(jì)原理,并從前端實(shí)現(xiàn)源代碼層面進(jìn)行了剖析,分享了用戶會(huì)話管理的各種 Boundary Condition(邊界情況)下實(shí)現(xiàn)的注意事項(xiàng)。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的浅谈电商网站开发中用户会话管理机制的设计和实现原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 谷歌将在开发者大会上发布PaLM 2 展
- 下一篇: 适合 Kubernetes 初学者的一些