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 技术原理

① 三大进程与三种场景

  • 将 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 进程同时进行变更操作。

四、总结

以上是关于iOS之深入解析WKWebView的cookie管理的主要内容,如果未能解决你的问题,请参考以下文章

iOS之深入解析WKWebView的WebKit源码调试与分析

iOS之WKWebView的坑点收录和优化处理

iOS开发:WKWebView的使用(设置cookie、不受信任的HTTPS、返回关闭按钮)

深入理解 WKWebView(基础篇)—— 聊聊 cookie 管理那些事

【iOS】WKWebView使用Cookies遇到的坑

iOS WKWebView学习总结