深入理解 WKWebView(基础篇)— 聊聊 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
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 操作:
@ interface WKHTTPCookieStore : NSObject
可以看到,不同场景下的 cookie 操作都是极其简单的,我们似乎已经通过简单的封装接口掌握了 cookie 技术,那么问题来了:
- cookie 究竟是存储在哪的?内存,还是磁盘?
- 三种不同场景的 cookie 操作是如何协同工作的?
现在,我们能回答这些问题吗?如果不能,请继续跟随我深入 WKWebView 源码,让代码告诉我们答案。
4. WebKit Cookie 技术原理
再次回到源码探索的道路:紧紧围绕 UIProcess、WebContent、NetworkProcess 三大进程进行理解。
4.1 三大进程与三种场景
如上图所示,我们将 cookie 操作的三种场景与三大进程进行关联,其中,
- 客户端操作在 UIProcess 进程(即我们的 app 进程),通过封装的 WKHTTPCookieStorage 进行操作。
- 前端 js 函数,通过 JSCore 解析执行后最终调用了 WebContent 进程中的 C++ 函数进行操作,如下所示:
virtual String cookies(Document&, const URL&) const;
- 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 有如下存储规则:
- allCookies:所有 cookie 都会存入字典 allCookies 中,方便快速查询。当我们杀死 app 后,位于内存中的 allCookies 字典也会一同清理掉。
- sessionOnly false cookie:对于某个 cookie,如果其属性中 sessionOnly 为 false,且设置的过期时间未到达,那我们判断该 cookie 是否具备持久性的逻辑如下:
let persistable = self.allCookies.filter { (_, value) in
- 持久性 cookie:具备持久性的 cookie 需要存储到磁盘文件中。存入路径规则如下:
let bundlePath = Bundle.main.bundlePath
问题二:三种不同场景的 cookie 操作是如何协同工作的?
如 cookie 管理协同图 所示,不同场景下的 cookie 协同操作其本质就是三大进程间的通信:
- UIProcess 进程并没有直接管理 cookie,而是通过进程间通信的方式,在 NetworkProcess 进程中管理 cookie。
- 所有 WebContent 进程都会注册监听 NetWorkProcess 中的 cookie 变更,及时进行相关变更的同步。
- 前端 setCookie 操作会将 cookie 字符串解析为 NSHTTPCookie 实例,然后将该 cookie 存入 cookieCache 中,并同步到 NetworkProcess 中进行存储。前端执行 getCookie 操作会读取当前页面域名下的所有 cookie,若判断 cookieCache 中没有当前页面域名下的 cookie,考虑到异常情况,会兜底向 NetworkProcess 发送请求进行 cookie 查找。
- 冷启动时,NetworkProcess 会初始化 NSHTTPCookieStorage ,并会将磁盘中的 cookie 读取出来,设置到内存字典 allCookies 中,同时将 allCookies 中的 cookie 变更通过广播的方式告知 WebContent 进程,发生了 cookie 变更,需要进行 cookie 同步。
- 来自客户端的 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/)。
参考链接
- WebKit 源码: https://github.com/WebKit/WebKit
- WebKit 官网: https://webkit.org/
- Apple 源码: https://github.com/apple
- MDN 官网: https://developer.mozilla.org/zh-CN
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论