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