深入理解 WKWebView(基礎(chǔ)篇)—— 聊聊 cookie 管理那些事
作者丨童紅明
來源丨百度App技術(shù)
1. 前言
在瀏覽內(nèi)核加載網(wǎng)絡(luò)資源的過程中我們離不開 HTTP 協(xié)議。它是在 Web 上進(jìn)行數(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ù)端都可以輕易的進(jìn)行增刪改查,我們在享受其便捷性的同時,也有必要確保其被正確、可控的使用。本文將在前系列文章的基礎(chǔ)上,繼續(xù)深入 WKWebView 源碼,聊聊 cookie 管理那些事,希望給大家?guī)硪恍┬碌囊暯呛驼J(rèn)知,揭開 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)、購物車、游戲分?jǐn)?shù)或其它需要記錄的信息。
個性化設(shè)置:如用戶自定義設(shè)置、主題等。
瀏覽器行為跟蹤:如跟蹤分析用戶行為等。
簡單介紹完 cookie的概念后,接下來我們再分別從前端、后端、客戶端的視角聊聊 cookie 的基本使用。
3. Cookie 基本使用
丨3.1 前端通過 js 操作 cookie
詳細(xì) cookie 格式語法參考 MDN 語法鏈接:https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie
// 讀取所有可從當(dāng)前頁面訪問的 cookieallCookies = document.cookie;// 寫一個新 cookiedocument.cookie = "someCookieName=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/";
丨3.2 后端配置 cookie
詳細(xì) 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 進(jìn)行 cookie 操作:
@interface WKHTTPCookieStore : NSObject/*! @abstract 獲取所有 cookie@param completionHandler 獲取所有 cookie 后回調(diào)*/- (void)getAllCookies:(void (^)(NSArray<NSHTTPCookie *> *))completionHandler;/*! @abstract 設(shè)置一個 cookie@param cookie 需要設(shè)置的 cookie@param completionHandler cookie 設(shè)置成功的回調(diào)*/- (void)setCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;/*! @abstract 刪除指定的 cookie@param completionHandler cookie 成功刪除的回調(diào)*/- (void)deleteCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;@end
可以看到,不同場景下的 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 三大進(jìn)程進(jìn)行理解。
丨4.1 三大進(jìn)程與三種場景

如上圖所示,我們將 cookie 操作的三種場景與三大進(jìn)程進(jìn)行關(guān)聯(lián),其中,
(1)客戶端操作在 UIProcess 進(jìn)程(即我們的 app 進(jìn)程),通過封裝的 WKHTTPCookieStorage 進(jìn)行操作。
(2)前端 js 函數(shù),通過 JSCore 解析執(zhí)行后最終調(diào)用了 WebContent 進(jìn)程中的 C++ 函數(shù)進(jìn)行操作,如下所示:
virtual String cookies(Document&, const URL&) const;virtual void setCookies(Document&, const URL&, const String& cookieString);
(3)WKWebView 中的網(wǎng)絡(luò)請求最終都是通過 NetworkProcess 中的 NSURLSession 管理的,服務(wù)端網(wǎng)絡(luò)響應(yīng)的 cookie 設(shè)置操作都在該進(jìn)程中完成。
丨4.2 三種場景下的協(xié)同工作

cookie 管理協(xié)同圖
如圖所示,描述了三大場景下 cookie 的協(xié)同管理,接下來,我們將結(jié)合該圖解答第二小節(jié)中提出的問題。
問題一:cookie 究竟是存儲在哪的?內(nèi)存,還是磁盤?
UIProcess:
UIProcess 進(jìn)程為 app 進(jìn)程(app 進(jìn)程中其實有 NSHTTPCookieStorage 倉儲進(jìn)行 cookie 管理,但這不是本文的重點,因此不展開來講),蘋果系統(tǒng)為開發(fā)者提供了 WKHTTPCookieStorage API 進(jìn)行 WebKit 內(nèi)核的 cookie 管理,WKHTTPCookieStorage 其實并不提供實際的存儲能力,而是封裝了一系列基于進(jìn)程間通信的方法,將 UIProcess 進(jìn)程中發(fā)生的 cookie 操作,發(fā)送到 NetworkProcess 進(jìn)程中進(jìn)行處理,并將執(zhí)行結(jié)果通過回調(diào)函數(shù)返回。
WebContent:
WebContent 進(jìn)程是前端操作 cookie 的進(jìn)程,原則上,每一個網(wǎng)頁頁面都只能操作當(dāng)前頁面域名下的cookie。因此基于性能考慮,每一個 WebContent 進(jìn)程中會有一個 cookieCache 實例,它是 NetworkProcess 進(jìn)程中存儲 cookie 的子集,僅存儲當(dāng)前頁面域名下的 cookie,因此 cookieCache 采取了內(nèi)存緩存的方式,其特征是存儲量小,查找速度快。
NetworkProcess:
NSHTTPCookieStorage setCookie 流程圖
NetworkProcess 進(jìn)程是 cookie 存儲的最核心進(jìn)程,它管理來自網(wǎng)絡(luò)中服務(wù)端 response 中配置的 cookie,同時也接受來自前端和客戶端的 cookie 操作,是最全的 cookie 存儲中心。通過源碼分析,我們發(fā)現(xiàn)其內(nèi)部還是通過 NSHTTPCookieStorage 進(jìn)行管理的, NSHTTPCookieStorage 有如下存儲規(guī)則:
(1)allCookies:所有 cookie 都會存入字典 allCookies 中,方便快速查詢。當(dāng)我們殺死 app 后,位于內(nèi)存中的 allCookies 字典也會一同清理掉。
(2)sessionOnly false cookie:對于某個 cookie,如果其屬性中 sessionOnly 為 false,且設(shè)置的過期時間未到達(dá),那我們判斷該 cookie 是否具備持久性的邏輯如下:
let persistable = self.allCookies.filter { (_, value) invalue.expiresDate != nil &&value.isSessionOnly == false &&value.expiresDate!.timeIntervalSinceNow > 0}
(3)持久性 cookie:具備持久性的 cookie 需要存儲到磁盤文件中。存入路徑規(guī)則如下:
let bundlePath = Bundle.main.bundlePathvar bundleName = bundlePath.components(separatedBy: "/").last!if let range = bundleName.range(of: ".", options: .backwards, range: nil, locale: nil) {bundleName = String(bundleName[..<range.lowerBound])}let cookieFolderPath = URL(fileURLWithPath: bundleName, relativeTo: FileManager.default.urls(for: .applicationSupportDirectory, in:.userDomainMask)[0]).pathcookieFilePath = filePath(path: cookieFolderPath, fileName: "/.cookies." + cookieStorageName, bundleName: bundleName)
問題二:三種不同場景的 cookie 操作是如何協(xié)同工作的?
如 cookie 管理協(xié)同圖 所示,不同場景下的 cookie 協(xié)同操作其本質(zhì)就是三大進(jìn)程間的通信:
(1)UIProcess 進(jìn)程并沒有直接管理 cookie,而是通過進(jìn)程間通信的方式,在 NetworkProcess 進(jìn)程中管理 cookie。
(2)所有 WebContent 進(jìn)程都會注冊監(jiān)聽 NetWorkProcess 中的 cookie 變更,及時進(jìn)行相關(guān)變更的同步。
(3)前端 setCookie 操作會將 cookie 字符串解析為 NSHTTPCookie 實例,然后將該 cookie 存入 cookieCache 中,并同步到 NetworkProcess 中進(jìn)行存儲。前端執(zhí)行 getCookie 操作會讀取當(dāng)前頁面域名下的所有 cookie,若判斷 cookieCache 中沒有當(dāng)前頁面域名下的 cookie,考慮到異常情況,會兜底向 NetworkProcess 發(fā)送請求進(jìn)行 cookie 查找。
(4)冷啟動時,NetworkProcess 會初始化 NSHTTPCookieStorage ,并會將磁盤中的 cookie 讀取出來,設(shè)置到內(nèi)存字典 allCookies 中,同時將 allCookies 中的 cookie 變更通過廣播的方式告知 WebContent 進(jìn)程,發(fā)生了 cookie 變更,需要進(jìn)行 cookie 同步。
(5)來自客戶端的 cookie 操作或者來自服務(wù)端的 cookie 設(shè)置,導(dǎo)致了 NetworkProcess 中的 cookie 變更,都會通過廣播的方式告知所有 WebContent 進(jìn)程同時進(jìn)行變更操作。
5. 總結(jié)
總而言之,cookie 操作簡單,使用方便,多端同學(xué)都經(jīng)常與其打交道。理清 WebKit 內(nèi)部的 cookie 管理方式讓我們在理論層面更了解 cookie 的技術(shù)原理。希望閱讀此文后,相關(guān)開發(fā)同學(xué)在日常工作中,如果與 cookie 打了交道,一定要考慮清楚修改帶來的影響面,謹(jǐn)慎操作。
NSHTTPCookieStorage 實現(xiàn)
NSHTTPCookieStorage 對應(yīng)的 swift 版本開源代碼如下, 里面有許多基礎(chǔ)類庫的設(shè)計思路,個人認(rèn)為非常有參考價值,有興趣的同學(xué)可以去研究相關(guān)實現(xiàn):
https://github.com/apple/swift-corelibs-foundation/blob/main/Sources/FoundationNetworking/HTTPCookieStorage.swift補(bǔ)充:跨域請求攜帶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
-End-
最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

面試題】即可獲取