【WKWebview】WKWebView Cookie 同步

个人实测:js注入的方式更靠谱一点

⌈iOS⌋WKWebView Cookie 同步的一种方式

屈服于 Apple 的“淫威”,开发者不得不将 App 的网页容器从 UIWebView 迁移到 WKWebView。我们在享受后者带来的性能和功能提升的同时,也被诸如 Cookie 同步、截图、白屏等问题弄得抓耳挠腮、狼狈不堪。无疑,上述问题会随着系统更新被逐渐修复,而开发者在产品系统适配的硬性要求下只得各凭本身缝缝补补,以期望减少在用户侧出现的问题。如题,下文我们对 Cookie 同步的情况进行一些说明,并总结出一种可行的方案。

问题回顾

简言之,WKWebView 的网络模块进程独立于 App 进程,App 进程通过 HTTPCookieStorage 管理的 Cookie 系统会自动使用 IPC 通信同步到 WKWebView。同 App 侧的 HTTPCookieStorage,WKWebView 的存储结构在代码层面表现为 WKWebsiteDataStore(iOS 9+) 和 WKHTTPCookieStore(iOS 11+),前者拥有后者。上述过程咋一看并不会有什么问题,但正因为是 IPC 来完成进程通信,它必然是异步执行的,即感官上的同步延迟。如果在这之前就进行需要验证 Cookie 的 WebView 请求,就是发现因缺少 Cookie 导致的鉴权失败。

一个典型的 🌰 是:用户通过通知等方式启动 App 打开一个和用户态关联的 Web URL,在用户登录完成之后(登录接口会进行 Set-Cookie 操作,写入用户的 auth_token 等数据)立即打开网页。此时会发现网页要求用户重新登录,如果通过 Charles 抓包进行观察的话,会发现此时 Cookies 中并没有上述 auth_token 等与用户态关联的鉴权信息。

换汤不换药的 WKHTTPCookieStore

从 iOS 11 开始,我们可以开始用官方的补救措施:WKHTTPCookieStore。它与 HTTPCookieStorage 接口很相似,我们或许很自然地想到用下面这样一段代码来同步 Cookie:

let group = DispatchGroup()
HTTPCookieStorage.shared.cookies?.forEach {group.enter()webView.configuration.websiteDataStore.httpCookieStore.setCookie($0) {group.leave()}
}
group.notify(queue: .main) {webView.load(urlRequest)
}

在实践过程的测试初期,并没有发现什么异常。而在内测阶段,**App 中存在很多的网页场景且每次加载之前都会进行上述的同步操作,就会几率性出现 setCookie 的 completionHandler 不执行的情况,从而导致当前网页且后续所有网页都无法正常加载,**从 WKHTTPCookieStore 设置 Cookie 的接口不难看出,它本质上还是通过 IPC 异步与 Web 进程通信。当同步的 Cookie 操作频繁执行时,会导致 App 与 Web 进程间通信出现异常,而这个度很难去把控,所以放弃这种方式。

这里有提到监听 NSHTTPCookieManagerCookiesChangedNotification 来修改 WKHTTPCookieStore 的方式,诸君可自行尝试。

从一而终:JS 注入

通过 Javascript 注入 Cookie 算是一种老生常谈的同步手段了,它分为以下两个个步骤:

  1. 所有的 WKWebView 公用一个 WKProcessPool(不透明类型)和 WKWebsiteDataStore,后者使用 WKWebsiteDataStore.default() 返回的实例。
  2. 使用 WKUserScript 向 WKWebView 的 WKUserContentController 中注入 Javascript:
private func _syncCookies() {// https://stackoverflow.com/a/32845148var scripts: [String] = ["var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } )"]// Cookie 过期处理由系统进行管理,不进行手动删除操作HTTPCookieStorage.shared.cookies?.forEach {// 假设系统的 Cookie 同步行为没有完成,如果过滤具有 httpOnly 标示的 cookie,就会在后续网络请求中 cookie 丢失的问题。// 即使在首次 loadRequest 中直接设置请求头的 cookie 字段注入该 cookie。// guard $0.isHTTPOnly == false else { return }// 当不存在此 cookie 时,才进行设置,避免注入同名 cookiescripts.append("if (cookieNames.indexOf('\($0.name)') == -1) { document.cookie='\($0.javaScriptString)'; }")}let source = scripts.joined(separator: ";\n")userContentController.addUserScript(WKUserScript(source: source,injectionTime: .atDocumentStart,forMainFrameOnly: false))
}
  1. 在用户退出登录时,清除 WKWebView 数据:
WKWebsiteDataStore.default().removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(),modifiedSince: Date(timeIntervalSince1970: 0.0),completionHandler: completion
)pool = nil// Important: 销毁所有的 WKWebView,如果有常驻 WKWebView 则重建。在实践工程中发现几率性出现 Cookie 残留,即使已经执行了清除所有数据的操作。

按部就班之后,你会发现这个方式和完全靠系统来处理存在同样的问题:存在延迟,具体表现为第一次打开网页 Cookie 没有,第二次打开又有了。WTF!!!事实上,这算是 WKUserScript 注入的一个坑了。注意看,我们注入 Javascript 的时机是 atDocumentStart,文档对它的解释是这样的:A constant to inject the script after the creation of the webpage’s document element, but before loading any other content.从字面理解来看,在 HTML DOM 加载过程中,WKWebView 的网络请求就会带上这些 Cookie,但实际上并没有。

从本质上来看,App 侧注入的 document.cookie 是给 Web 侧增加 Cookie 环境。而从上面的操作结果来看,并不代表它在注入完成之后就会立刻生效。在此,我们可以在后台预先加载一个 URL(指向一个空白的能正常加载成功的页面即可,如 xxxx://xxxx/ios/cookie/sync)来完成 Cookie 环境的同步,然后再请求实际的 Web URL 来解决这个问题。

总结

从 UIWebView 时期与 App 公用 HTTPCookieStorage 到现在 WKWebView 的泾渭分明,最可靠的 Cookie 同步方式始终都是系统默认方案。无奈后者的技术架构迫使我们做一些“骚操作”去促使系统自动完成,全文单从 App 侧同步到 Web 侧的一些实践过程进行了阐述,最终总结出 Preload + Javascript + Clean 的一种基本可行的方式。而从 Web 侧到 App 侧,没有做任何额外的操作,而是完全依赖于系统行为,这或许埋下了一些隐患,但到目前来说我们没有遇到相关问题。其实,针对于服务器渲染的前端页面,在充分考虑安全性的前提下,将 Cookie 直接塞到 URL 的 query 中是一种更为直接简单的方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/54728.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity3D Shader的阴影部分法线效果详解

在Unity3D开发中,阴影处理是提升场景真实感和视觉质量的重要一环。法线贴图(Normal Mapping)作为一种高效的纹理映射技术,在增强模型表面细节和凹凸感方面扮演着重要角色。本文将详细解析UnityShader中阴影部分的法线效果&#xf…

【fastjson】json对象格式化打印

为了让日志打印时以格式化的JSON输出,你可以将input.toJSONString()调用改为使用格式化输出的方式。FastJSON库的toJSONString方法支持格式化输出,你可以传入true参数实现这一点。具体修改如下:关键要用JSONObject.toJSONString 来实现:toJSONString(true) 会把true作为对象…

springMVC添加webapp

项目结构-->模块-->找到想添加的模块下的web 点击号 添加路径 会在.../src/main/目录下自动生成目录

【代码笔记】

结构体 /*C04.L10.结构体 张其博 2024.9.19 */ #include<bits/stdc.h> using namespace std; //1.定义 /*struct 结构体名 { 成员表 //可以有多个成员成员函数 //可以有多个成员函数&#xff0c;也可以没有 } 结构体变量表&#xff1b; //可以同时定义多个结构体变量…

使用socket编程来实现一个简单的C/S模型(TCP协议)

前置 所使用到的函数查看本专栏中&#xff1a;socket的概念和常用函数介绍 socket的概念和常用函数介绍-CSDN博客 1.C/S模型 - TCP 下图是基于TCP协议的客户端/服务器程序的一般流程&#xff1a; 服务器调用socket()、bind()、listen()完成初始化后&#xff0c;调用accept(…

基于SpringBoot+Vue+Uniapp的仓库点单小程序的详细设计和实现

2. 详细视频演示 文章底部名片&#xff0c;联系我获取更详细的演示视频 3. 论文参考 4. 项目运行截图 代码运行效果图 代码运行效果图 代码运行效果图 代码运行效果图代码运行效果图 代码运行效果图 5. 技术框架 5.1 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发…

linux中sed命令详解

sed 是 Linux 中的一个流编辑器&#xff08;stream editor&#xff09;&#xff0c;主要用于处理文本的编辑和转换。它可以从文件或标准输入读取内容&#xff0c;然后根据指定的模式和指令对数据进行处理&#xff0c;最后输出修改后的结果。它的强大之处在于可以通过脚本或命令…

PowerJob做定时任务调度

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、区别对比二、使用步骤1. 定时任务类型2.PowerJob搭建与部署 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; PowerJob是基于java开…

自动驾驶系列—GPS技术在自动驾驶中的应用与挑战:全面解析

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

axios 的 get 请求传参数

在使用 Axios 发起 GET 请求时&#xff0c;参数通常是通过 URL 的查询字符串来传递的。Axios 提供了一个简洁的接口来构建这样的请求&#xff0c;并自动将参数附加到 URL 上。 以下是一个使用 Axios 发起 GET 请求并传递参数的示例&#xff1a; const axios require(axios);…

【含开题报告+文档+PPT+源码】基于SpringBoot+Vue医药知识学习与分享平台的设计与实现

开题报告 本论文介绍了一个名为岐黄之家的知识学习与分享平台的设计与实现。该平台旨在为用户提供一个交流、学习和分享医药知识的空间。论文首先介绍了中医院交流平台的背景和相关研究现状。随着互联网的快速发展&#xff0c;中医学的学习和交流需求逐渐增多&#xff0c;因此…

linux 配置nfs

服务器端 sudo apt update sudo apt-get install nfs-kernel-server配置NFS服务器 mkdir /home/aa/workspace/nfsdir chmod 777 /home/aa/workspace/nfsdir sudo vim /etc/exports添加这个语句 /home/aa/workspace/nfsdir *(rw,sync,no_root_squash,insecure)sudo systemctl …

Python 如何使用 SQLAlchemy 进行复杂查询

Python 如何使用 SQLAlchemy 进行复杂查询 一、引言 SQLAlchemy 是 Python 生态系统中非常流行的数据库处理库&#xff0c;它提供了一种高效、简洁的方式与数据库进行交互。SQLAlchemy 是一个功能强大的数据库工具&#xff0c;支持结构化查询语言&#xff08;SQL&#xff09;…

AI绘画 Liveportrait视频驱动图片 ComfyUI工作流详细部署教程(附资源包+详细报错排查)

AI绘画技术已经逐渐成为艺术创作的新趋势。现在&#xff0c;ComfyUI推出了Liveportrait视频驱动图片的AI绘画工作流&#xff0c;帮助你轻松实现AI绘画创作。本文将为你提供详细的部署教程&#xff0c;附上资源包和报错排查&#xff0c;让你快速上手AI绘画。 Liveportrait视频驱…

速盾:cdn经常换ip有利于SEO吗?

CDN&#xff08;Content Delivery Network&#xff09;是一种通过将网站的静态资源分布在全球各个服务器上&#xff0c;以加快用户访问速度和提高网站的可用性的技术。由于CDN网络中的服务器分布较广&#xff0c;因此在实际应用中&#xff0c;CDN服务商会不断对自己的服务器IP进…

栈的操作算法实现(数据结构)

1.实验目的 验证性实验&#xff1a;实现顺序栈各种基本运算的算法掌握栈的存储结构的表示和实现方法。 目的&#xff1a;领会顺序栈存储结构和掌握顺序栈中各种基本运算算法设计。 2.实验内容 验证性实验内容&#xff1a;编写一个程序sqstack.cpp,实现顺序栈(假设栈中元素类型…

springboot系列--web相关知识探索四

一、前言 web相关知识探索三中研究了请求中所带的参数是如何映射到接口参数中的&#xff0c;也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、以及自定义对象参数。web相关知识探索三中主要研究了注解方式以及Servlet API方式。本次…

决策树随机森林-笔记

决策树 1. 什么是决策树&#xff1f; 决策树是一种基于树结构的监督学习算法&#xff0c;适用于分类和回归任务。 根据数据集构建一棵树&#xff08;二叉树或多叉树&#xff09;。 先选哪个属性作为向下分裂的依据&#xff08;越接近根节点越关键&#xff09;&#xff1f;…

Node脚本实现批量打包Vue项目(child_process子进程、window)

前言 前几天用pnpmworkspace实现了monorepo&#xff0c;也就是单仓库多个项目&#xff0c;并且互相之间可能存在一定的联系。所以就存在一个打包的问题&#xff0c;也就是说&#xff0c;我想在打包某个特定子项目时&#xff0c;其他项目也执行build的命令。主要用到的是node的…

HDLBits中文版,标准参考答案 | 3.2.5 Finite State Machines | 有限状态机(2)

关注 望森FPGA 查看更多FPGA资讯 这是望森的第 17 期分享 作者 | 望森 来源 | 望森FPGA 目录 1 Lemmings 1 2 Lemmings 2 3 Lemmings 3 4 Lemmings 4 5 One-hot FSM | 独热 FSM 6 PS/2 packet parser | PS/2 数据包解析器 7 PS/2 packet parser anddatapath | PS/2 数…