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