iOS之深入解析WKWebView的cookie管理
Posted Forever_wj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS之深入解析WKWebView的cookie管理相关的知识,希望对你有一定的参考价值。
一、Cookie 概述
- 在浏览内核加载网络资源的过程中,往往离不开 HTTP 协议,它是在 Web 上进行数据交换的基础,同时也是一种无状态的 client-server 协议,这种无状态的属性促使许多端存储技术产生,其中最重要的技术之一就是 cookie 存储技术,它能方便的将数据存储于客户端,且在每次请求中都会在请求头中携带 cookie 数据并发送给 server。
- cookie 技术的便捷性使得它在多种场景中被广泛使用,有时候甚至存在滥用情况,对同一 cookie 实例,前端、客户端、服务端都可以轻易的进行增删改查,在享受其便捷性的同时,也有必要确保其被正确和可控的使用。
- MDN 官网对 cookie(Using HTTP cookies)的说明如下:
-
- HTTP cookie(也叫 Web cookie 或浏览器 cookie)是保存在浏览器本地的一小块数据,它会在浏览器向服务器发起请求时被携带并发送到服务器上。
-
- 通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态,cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为可能。
- cookie 主要用于以下三个方面:
-
- 会话状态管理:如用户登录状态、购物车、游戏分数或其它需要记录的信息。
-
- 个性化设置:如用户自定义设置、主题等。
-
- 浏览器行为跟踪:如跟踪分析用户行为等。
二、Cookie 基本使用
① 前端通过 js 操作 cookie
- cookie 格式语法,请参考 MDN 语法链接:Document.cookie。
- JS 设置 cookie:假设在 A 页面中要保存变量 userName 的值(“jack”)到 cookie 中,key 值为 name,则相应的 JS 代码为:
document.cookie="name="+userName;
- JS 读取 cookie:假设 cookie 中存储的内容为:name=jack; password=123
则在 B 页面中获取变量 userName 的值的 JS 代码如下:
var userName=document.cookie.split(";")[0].split("=")[1];
// JS操作cookies方法
// 写cookies
function setCookie(name,value)
{
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days*24*60*60*1000);
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
}
- 读取 cookies:
function getCookie(name) {
var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
- 删除 cookies:
function delCookie(name) {
var exp = new Date();
exp.setTime(exp.getTime() - 1);
var cval=getCookie(name);
if(cval!=null)
document.cookie= name + "="+cval+";expires="+exp.toGMTString();
}
// 使用示例
setCookie("name","hayden");
alert(getCookie("name"));
// 如果需要设定自定义过期时间
// 那么把上面的setCookie 函数换成下面两个函数就ok;
// 程序代码
function setCookie(name,value,time) {
var strsec = getsec(time);
var exp = new Date();
exp.setTime(exp.getTime() + strsec*1);
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
}
function getsec(str) {
alert(str);
var str1=str.substring(1,str.length)*1;
var str2=str.substring(0,1);
if (str2=="s") {
return str1*1000;
} else if (str2=="h") {
return str1*60*60*1000;
} else if (str2=="d") {
return str1*24*60*60*1000;
}
}
// 这是有设定过期时间的使用示例:
// s20是代表20秒
// h是指小时,如12小时则是:h12
// d是天数,30天则:d30
setCookie("name","hayden","s20");
② 后端配置 cookie
- 详细 cookie 格式语法参考 MDN 语法:HTTP cookies。
- 在 response header 中返回需要种到端上的 cookie,可以通过 Charles 工具抓包可以看到 header 中如下信息:
③ 客户端操作 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
三、WebKit Cookie 技术原理
① 三大进程与三种场景
- 之前,我们已经了解了 WebKit 源码调试与分析和 WKWebView 加载的生命周期与代理方法,紧紧围绕 UIProcess、WebContent、NetworkProcess三大进程进行理解,具体请参考:iOS之深入解析WKWebView加载的生命周期与代理方法。
- 将 cookie 操作的三种场景与三大进程进行关联:
-
- 客户端操作在 UIProcess 进程(即我们的 App 进程),通过封装的 WKHTTPCookieStorage 进行操作;
-
- 前端 js 函数,通过 JSCore 解析执行后最终调用了 WebContent 进程中的 C++ 函数进行操作,如下所示:
virtual String cookies(Document&, const URL&) const;
virtual void setCookies(Document&, const URL&, const String& cookieString);
-
- WKWebView 中的网络请求最终都是通过 NetworkProcess 中的 NSURLSession 管理的,服务端网络响应的 cookie 设置操作都在该进程中完成。
② 三种场景下的协同工作
- 三大场景下 cookie 的协同管理,如下所示:
- cookie 究竟是存储在哪的?内存,还是磁盘?
-
- UIProcess 进程为 App 进程(App 进程中其实有 NSHTTPCookieStorage 仓储进行 cookie 管理),苹果系统为开发者提供了 WKHTTPCookieStorage API 进行 WebKit 内核的 cookie 管理,WKHTTPCookieStorage 其实并不提供实际的存储能力,而是封装了一系列基于进程间通信的方法,将 UIProcess 进程中发生的 cookie 操作,发送到 NetworkProcess 进程中进行处理,并将执行结果通过回调函数返回。
-
- WebContent 进程是前端操作cookie 的进程,原则上,每一个网页页面都只能操作当前页面域名下的 cookie。因此基于性能考虑,每一个 WebContent 进程中会有一个 cookieCache 实例,它是 NetworkProcess 进程中存储 cookie 的子集,仅存储当前页面域名下的 cookie,因此 cookieCache 采取了内存缓存的方式,其特征是存储量小,查找速度快。
-
- NetworkProcess 进程是 cookie 存储的最核心进程,它管理来自网络中服务端 response 中配置的 cookie,同时也接受来自前端和客户端的 cookie 操作,是最全的 cookie 存储中心。通过源码分析,发现其内部还是通过 NSHTTPCookieStorage 进行管理的, NSHTTPCookieStorage setCookie 流程如下:
-
- NSHTTPCookieStorage 有如下存储规则:
-
-
- allCookies:所有 cookie 都会存入字典 allCookies 中,方便快速查询。当杀死 App 后,位于内存中的 allCookies 字典也会一同清理掉。
-
-
-
- sessionOnly false cookie:对于某个 cookie,如果其属性中 sessionOnly 为 false,且设置的过期时间未到达,那么应该判断该 cookie 是否具备持久性的逻辑,逻辑如下:
-
let persistable = self.allCookies.filter { (_, value) in
value.expiresDate != nil &&
value.isSessionOnly == false &&
value.expiresDate!.timeIntervalSinceNow > 0
}
-
-
- 持久性 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 协同操作其本质就是三大进程间的通信。
-
- 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 进程同时进行变更操作。
四、总结
- NSHTTPCookieStorage 对应的 swift 版本开源代码如下,里面有许多基础类库的设计思路,详情请参考:swift-corelibs-foundation。
- 基于安全考虑,iOS14 系统禁止了跨域请求携带 cookie:Tracking Prevention in WebKit。
以上是关于iOS之深入解析WKWebView的cookie管理的主要内容,如果未能解决你的问题,请参考以下文章
iOS之深入解析WKWebView的WebKit源码调试与分析
iOS开发:WKWebView的使用(设置cookie、不受信任的HTTPS、返回关闭按钮)