1. 前言
在瀏覽內(nèi)核加載網(wǎng)絡(luò)資源的過程中我們離不開 HTTP 協(xié)議。它是在 Web 上進行數(shù)據(jù)交換的基礎(chǔ),同時也是一種無狀態(tài)的 client-server 協(xié)議。這種無狀態(tài)的屬性促使許多端存儲技術(shù)產(chǎn)生,其中最重要的技術(shù)之一就是** cookie 存儲技術(shù)**,它能方便的將數(shù)據(jù)存儲于客戶端,且在每次請求中都會在請求頭中攜帶 cookie 數(shù)據(jù)并發(fā)送給 server。
cookie 技術(shù)的便捷性使得它在多種場景中被廣泛使用,有時候甚至存在濫用情況,對同一 cookie 實例,前端、客戶端、服務(wù)端都可以輕易的進行增刪改查,我們在享受其便捷性的同時,也有必要確保其被正確、可控的使用。本文將在前系列文章的基礎(chǔ)上,繼續(xù)深入 WKWebView 源碼,聊聊 cookie 管理那些事,希望給大家?guī)硪恍┬碌囊暯呛驼J知,揭開 cookie 管理的迷霧。
2. Cookie 概述
MDN官網(wǎng)(https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)對cookie的介紹如下:
HTTP cookie(也叫 Web cookie 或瀏覽器 cookie)是保存在瀏覽器本地的一小塊數(shù)據(jù),它會在瀏覽器向服務(wù)器發(fā)起請求時被攜帶并發(fā)送到服務(wù)器上。通常,它用于告知服務(wù)端兩個請求是否來自同一瀏覽器,如保持用戶的登錄狀態(tài)。cookie 使基于無狀態(tài)的 HTTP 協(xié)議記錄穩(wěn)定的狀態(tài)信息成為了可能。
cookie 主要用于以下三個方面:
會話狀態(tài)管理:如用戶登錄狀態(tài)、購物車、游戲分數(shù)或其它需要記錄的信息。
個性化設(shè)置:如用戶自定義設(shè)置、主題等。
瀏覽器行為跟蹤:如跟蹤分析用戶行為等。
簡單介紹完 cookie的概念后,接下來我們再分別從前端、后端、客戶端的視角聊聊 cookie 的基本使用。
3. Cookie 基本使用
丨3.1 前端通過 js 操作 cookie
詳細 cookie 格式語法參考 MDN 語法鏈接:https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie
// 讀取所有可從當前頁面訪問的 cookie
丨3.2 后端配置 cookie
詳細 cookie 格式語法參考 MDN 語法鏈接:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies
在 response header 中返回需要種到端上的 cookie ,我們通過 Charles 工具抓包可以看到 header 中如下信息:
丨3.3 客戶端操作 cookie
iOS 系統(tǒng)在 WKHTTPCookieStorage 類中提供如下 API 進行 cookie 操作:
@interface WKHTTPCookieStore : NSObject
可以看到,不同場景下的 cookie 操作都是極其簡單的,我們似乎已經(jīng)通過簡單的封裝接口掌握了 cookie 技術(shù),那么問題來了:
(1)cookie 究竟是存儲在哪的?內(nèi)存,還是磁盤?
(2)三種不同場景的 cookie 操作是如何協(xié)同工作的?
現(xiàn)在,我們能回答這些問題嗎?如果不能,請繼續(xù)跟隨我深入 WKWebView 源碼,讓代碼告訴我們答案。
4. WebKit Cookie 技術(shù)原理
再次回到源碼探索的道路,現(xiàn)在我們再回顧一下在《深入理解 WKWebView(入門篇)—— WebKit 源碼調(diào)試與分析》提及的源碼探索的核心技巧:緊緊圍繞 UIProcess、WebContent、NetworkProcess 三大進程進行理解。
丨4.1 三大進程與三種場景
如上圖所示,我們將 cookie 操作的三種場景與三大進程進行關(guān)聯(lián),其中,
(1)客戶端操作在 UIProcess 進程(即我們的 app 進程),通過封裝的 WKHTTPCookieStorage 進行操作。
(2)前端 js 函數(shù),通過 JSCore 解析執(zhí)行后最終調(diào)用了 WebContent 進程中的 C++ 函數(shù)進行操作,如下所示:
virtual String cookies(Document&, const URL&) const;
(3)WKWebView 中的網(wǎng)絡(luò)請求最終都是通過 NetworkProcess 中的 NSURLSession 管理的,服務(wù)端網(wǎng)絡(luò)響應(yīng)的cookie 設(shè)置操作都在該進程中完成。
丨4.2 三種場景下的協(xié)同工作
cookie 管理協(xié)同圖
如圖所示,描述了三大場景下 cookie 的協(xié)同管理,接下來,我們將結(jié)合該圖解答第二小節(jié)中提出的問題。
問題一:cookie 究竟是存儲在哪的?內(nèi)存,還是磁盤?
UIProcess:
UIProcess 進程為 app 進程(app 進程中其實有 NSHTTPCookieStorage 倉儲進行 cookie 管理,但這不是本文的重點,因此不展開來講),蘋果系統(tǒng)為開發(fā)者提供了 WKHTTPCookieStorage API 進行 WebKit 內(nèi)核的 cookie 管理,WKHTTPCookieStorage 其實并不提供實際的存儲能力,而是封裝了一系列基于進程間通信的方法,將 UIProcess 進程中發(fā)生的 cookie 操作,發(fā)送到 NetworkProcess 進程中進行處理,并將執(zhí)行結(jié)果通過回調(diào)函數(shù)返回。
WebContent:
WebContent 進程是前端操作 cookie 的進程,原則上,每一個網(wǎng)頁頁面都只能操作當前頁面域名下的cookie。因此基于性能考慮,每一個 WebContent 進程中會有一個 cookieCache 實例,它是 NetworkProcess 進程中存儲 cookie 的子集,僅存儲當前頁面域名下的 cookie,因此 cookieCache 采取了內(nèi)存緩存的方式,其特征是存儲量小,查找速度快。
NetworkProcess:
NSHTTPCookieStorage setCookie 流程圖
NetworkProcess 進程是 cookie 存儲的最核心****進程,它管理來自網(wǎng)絡(luò)中服務(wù)端 response 中配置的 cookie,同時也接受來自前端和客戶端的 cookie 操作,是最全的 cookie 存儲中心。通過源碼分析,我們發(fā)現(xiàn)其內(nèi)部還是通過NSHTTPCookieStorage 進行管理的, NSHTTPCookieStorage 有如下存儲規(guī)則:
(1)allCookies:所有 cookie 都會存入字典 allCookies 中,方便快速查詢。當我們殺死 app 后,位于內(nèi)存中的 allCookies 字典也會一同清理掉。
(2)sessionOnly false cookie:對于某個 cookie,如果其屬性中 sessionOnly 為 false,且設(shè)置的過期時間未到達,那我們判斷該 cookie 是否具備持久性的邏輯如下:
let persistable = self.allCookies.filter { (_, value) in
(3)持久性 cookie:具備持久性的 cookie 需要存儲到磁盤文件中。存入路徑規(guī)則如下:
let bundlePath = Bundle.main.bundlePath
問題二:三種不同場景的 cookie 操作是如何協(xié)同工作的?
如 cookie 管理協(xié)同圖 所示,不同場景下的 cookie 協(xié)同操作其本質(zhì)就是三大進程間的通信:
(1)UIProcess 進程并沒有直接管理 cookie,而是通過進程間通信的方式,在 NetworkProcess 進程中管理 cookie。
(2)所有 WebContent 進程都會注冊監(jiān)聽 NetWorkProcess 中的 cookie 變更,及時進行相關(guān)變更的同步。
(3)前端 setCookie 操作會將 cookie 字符串解析為 NSHTTPCookie 實例,然后將該 cookie 存入 cookieCache 中,并同步到 NetworkProcess 中進行存儲。前端執(zhí)行 getCookie 操作會讀取當前頁面域名下的所有 cookie,若判斷 cookieCache 中沒有當前頁面域名下的 cookie,考慮到異常情況,會兜底向 NetworkProcess 發(fā)送請求進行 cookie查找。
(4)冷啟動時,NetworkProcess 會初始化 NSHTTPCookieStorage ,并會將磁盤中的 cookie 讀取出來,設(shè)置到內(nèi)存字典 allCookies 中,同時將 allCookies 中的 cookie 變更通過廣播的方式告知 WebContent 進程,發(fā)生了 cookie變更,需要進行 cookie 同步。
(5)來自客戶端的 cookie 操作或者來自服務(wù)端的 cookie 設(shè)置,導(dǎo)致了 NetworkProcess 中的 cookie 變更,都會通過廣播的方式告知所有 WebContent 進程同時進行變更操作。
5. 總結(jié)
總而言之,cookie 操作簡單,使用方便,多端同學(xué)都經(jīng)常與其打交道。理清 WebKit 內(nèi)部的 cookie 管理方式讓我們在理論層面更了解 cookie 的技術(shù)原理。希望閱讀此文后,相關(guān)開發(fā)同學(xué)在日常工作中,如果與 cookie 打了交道,一定要考慮清楚修改帶來的影響面,謹慎操作。
NSHTTPCookieStorage 實現(xiàn)
NSHTTPCookieStorage 對應(yīng)的 swift 版本開源代碼如下, 里面有許多基礎(chǔ)類庫的設(shè)計思路,個人認為非常有參考價值,有興趣的同學(xué)可以去研究相關(guān)實現(xiàn):
補充:跨域請求攜帶cookie
基于安全考慮,iOS14系統(tǒng)禁止了跨域請求攜帶cookie(https://webkit.org/tracking-prevention/)。
敬請期待:
深入理解 WKWebView(基礎(chǔ)篇)-- 探究 WebKit 網(wǎng)絡(luò)資源緩存
參考鏈接:
WebKit 源碼:https://github.com/WebKit/WebKit
WebKit 官網(wǎng):https://webkit.org/
Apple 源碼:https://github.com/apple
MDN官網(wǎng):https://developer.mozilla.org/zh-CN