聊聊 cookie 管理那些事

2023-11-05

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 操作:

@interface WKHTTPCookieStore : NSObject

/*! @abstract 获取所有 cookie
 @param completionHandler 获取所有 cookie 后回调
 */
- (void)getAllCookies:(void (^)(NSArray<NSHTTPCookie *> *))completionHandler;


/*! @abstract 设置一个 cookie 
 @param cookie 需要设置的 cookie 
 @param completionHandler cookie 设置成功的回调 
 */ 
- (void)setCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;


/*! @abstract 删除指定的 cookie 
 @param completionHandler cookie 成功删除的回调 
 */ 
- (void)deleteCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;

@end

可以看到,不同场景下的 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) in
            value.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 管理那些事

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

聊聊 cookie 管理那些事 的相关文章

  • 使用Ajax加载数据的dataTables

    dataTables是一种很好用前端表格显示库 当加载大量数据时 可以用Ajax 获取数据来提高效率 增速网页加载速率 下面以一个例子作示范 首先 需要下载jquery以及dataTables库 这里使用的是版本是jQuery v1 11

随机推荐

  • 206. Reverse Linked List

    Definition for singly linked list struct ListNode int val ListNode next ListNode int x val x next NULL class Solution pu
  • Ansys workbench分析应用基础(6)

    圣维南原理和模型简化 如上图所示 我们对长度分别为100mm 150mm和200mm的板子添加完全均布载荷 二分之一均布载荷和线载荷 载荷的值相同 统计出不同模型不同情况所对应的应力最大值 如下图所示 从图中 我们可以看出对于板长为200m
  • 2023年5月份中国电子学会青少年软件编程(C语言)等级考试一级真题讲解

    1 输出第二个整数 题目描述 输入三个整数 把第二个输入的整数输出 输入 只有一行 共三个整数 整数之间由一个空格分隔 整数是32位有符号整数 输出 只有一行 一个整数 即输入的第二个整数 样例输入 123 456 789 样例输出 456
  • jsonp原理详解

    JSONP 全称 JSON with padding 是一种跨域请求的方法 它允许在不受限制地从一个域名获取数据并在另一个域名下使用该数据 JSONP 的原理是通过动态创建 script 标签来实现 在客户端向服务器发送请求时 服务器返回
  • 面试宝典----数据库(总结来自知乎路人甲)

    一 什么是存储过程 有哪些优缺点 存储过程是一些预编译的SQL语句 更加直白的理解 存储过程可以说是一个记录集 它是由一些T SQL语句组成的代码块 这些T SQL语句代码像一个方法一样实现一些功能 对单表或多表的增删改查 然后再给这个代码
  • svn 打patch

    patch patch 即 补丁 的意思 当代码有改动的时候 svn会产生diff 可以查看diff和打patch 使用Mac终端来打patch也是非常方便的 首先查看本地的修改 确认无误后 使用 svn diff gt PATCH 命令可
  • C++ main函数中参数argc和argv含义及用法

    argc 是 argument count的缩写 表示传入main函数的参数个数 argv 是 argument vector的缩写 表示传入main函数的参数序列或指针 并且第一个参数argv 0 一定是程序的名称 并且包含了程序所在的完
  • 2022美赛C题思路分享

    美赛c题 比特币和金子投资分析 问题翻译 下附思路 1 问题分析 本题题目理解较为简单 就是利用历史数据对于投资策略的分析 每一天的决策只能使用之前的历史数据 求解最佳的投资回报 并分析模型的可行性 2模型准备 时间序列分析模型选择 以及模
  • 学习实践-Whisper语音识别模型实战(部署+运行)

    1 Whisper内容简单介绍 OpenAI的语音识别模型Whisper Whisper 是一个自动语音识别 ASR Automatic Speech Recognition 系统 OpenAI 通过从网络上收集了 68 万小时的多语言 9
  • 【matplotlib】饼图+legend()、loc、color位置颜色图例中文显示(一个饼图的例子)

    博客已经搬家到 捕获完成 https www v2python com 1 原来自己做的饼图 http mp blog csdn net postedit 79222127 见文章 matplotlib 中文显示 负号显示 统计微信好友性别
  • 《再也不怕elasticsearch》Spring Boot集成Elasticsearch

    大家好我是迷途 一个在互联网行业 摸爬滚打的学子 热爱学习 热爱代码 热爱技术 热爱互联网的一切 再也不怕elasticsearch系列 帅途会慢慢由浅入深 为大家剖析一遍 各位大佬请放心 虽然这个系列帅途有时候更新的有点慢 但是绝对不会烂
  • django获取某一个字段的列表,values/values_list/flat

    django获取某一个字段的列表 values values list flat 2017年11月01日 11 43 28 阅读数 2241 python view plain copy class Building models Mode
  • C语言实现邻接矩阵(无向图的顺序表示)

    文章目录 有向 无向不带权图 带权图 定义图的结构体 初始化 分析 分配堆空间 对矩阵的行开辟空间 对矩阵 即二维数组 进行初始化 edge 0 0 edge 0 9 edge 1 0 edge 1 9 edge 2 0 edge 2 9
  • 全国物流快递查询网址大全

    http www kiees cn default htm
  • VMware&Linux详细安装步骤

    VMware Linux详细安装步骤 一 VmWare虚拟机的安装 1 安装虚拟机 注意 虚拟机安装完成后会在网络连接中多出两个虚拟网卡 二 在虚拟机上安装CentOS 1 创建新虚拟机 文件 新建虚拟机 或 直接点击 创建新的虚拟机 图标
  • python1_2列表(2)

    列表增删改查 1 增 all in list 0 3 hello True all in list append hello world 新增元素 print all in list 运行结果 2 插入 all in list 0 3 he
  • 机器学习-基础

    欢迎来到机器学习的世界 博客主页 卿云阁 欢迎关注 点赞 收藏 留言 本文由卿云阁原创 本阶段属于练气阶段 希望各位仙友顺利完成突破 首发时间 2021年5月5日 希望可以和大家一起完成进阶之路 作者水平很有限 如果发现错误 请留言轰炸哦
  • Java学习心得4——Java中的包是什么

    Java中的包完全可以理解成一个文件夹 如果你不信 我们可以做一些测试 1 我们先在eclipse中创建一个项目 java Project 命名为test 2 我们可以在文件资源管理器的中找到这个项目 3 我们双击进入test文件夹 再进入
  • 系统架构设计专业技能 · 信息安全技术

    点击进入系列文章目录 现在的一切都是为将来的梦想编织翅膀 让梦想在现实中展翅高飞 Now everything is for the future of dream weaving wings let the dream fly in re
  • 聊聊 cookie 管理那些事

    1 前言 在浏览内核加载网络资源的过程中我们离不开 HTTP 协议 它是在 Web 上进行数据交换的基础 同时也是一种无状态的 client server 协议 这种无状态的属性促使许多端存储技术产生 其中最重要的技术之一就是 cookie