【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 算是一种老生常谈的同步手段了,它分为以下两个个步骤:
- 所有的 WKWebView 公用一个 WKProcessPool(不透明类型)和 WKWebsiteDataStore,后者使用 WKWebsiteDataStore.default() 返回的实例。
- 使用 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))
}
- 在用户退出登录时,清除 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 中是一种更为直接简单的方式。
相关文章:
【WKWebview】WKWebView Cookie 同步
个人实测:js注入的方式更靠谱一点 ⌈iOS⌋WKWebView Cookie 同步的一种方式 屈服于 Apple 的“淫威”,开发者不得不将 App 的网页容器从 UIWebView 迁移到 WKWebView。我们在享受后者带来的性能和功能提升的同时,也被诸如 Cookie 同步、截图…...
vue-router拦截器
在 Vue 项目中,vue-router 的路由拦截器和组件内部的路由拦截器(如 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave)虽然都能拦截路由,但它们的作用范围和使用场景有所不同。下面是二者的区别总结: 1. 全局路…...
SpringBoot驱动的人事管理系统:高效办公新选择
1系统概述 1.1 研究背景 如今互联网高速发展,网络遍布全球,通过互联网发布的消息能快而方便的传播到世界每个角落,并且互联网上能传播的信息也很广,比如文字、图片、声音、视频等。从而,这种种好处使得互联网成了信息传…...

大数据干了什么?
1.大数据技术主要解决的问题是海量数据的 存储 和 查询...
android studio可用下载地址
AndroidDevTools - Android开发工具 Android SDK下载 Android Studio下载 Gradle下载 SDK Tools下载 在此记录一下...

HTTP 协议详解
HTTP 协议是 Web 的基石,它定义了客户端和服务器之间的通信规则。本文将深入地探讨 HTTP 的核心概念,包括工作原理、请求方法、状态码以及不同 HTTP 版本的演进。 一、HTTP 的工作原理 HTTP 协议基于客户端-服务器模型,遵循请求-响应的循环&…...
【力扣 | SQL题 | 每日四题】力扣534, 574, 2314, 2298
今天的每日四题比较简单,主要其中两题可以用窗口函数轻松解决。 1. 力扣534:游戏玩法分析3 1.1 题目: 表:Activity ----------------------- | Column Name | Type | ----------------------- | player_id | int | …...

Gitxray:一款基于GitHub REST API的网络安全工具
关于Gitxray Gitxray是一款基于GitHub REST API的网络安全工具,支持利用公共 GitHub REST API 进行OSINT、信息安全取证和安全检测等任务。 Gitxray(Git X-Ray 的缩写)是一款多功能安全工具,专为 GitHub 存储库而设计。它可以用于…...

Chrome(谷歌)浏览器 数据JSON格式美化 2024显示插件安装和使用
文章目录 目录 文章目录 安装流程 小结 概要安装流程技术细节小结 概要 没有美化的格式浏览器展示 美化之后效果图 安装流程 下载地址 https://github.com/gildas-lormeau/JSONVue 点击下载 下载成功,如图所示 解压文件 添加成功,如图所示 通过浏览器…...

关于相机的一些零碎知识点
热成像,英文为Thermal Imaging,例如型号500T,其实指的就是热成像500分辨率。 相机的CMOS,英文为Complementary Metal Oxide Semiconductor,是数码相机的核心成像部件,是一种互补金属氧化物导体器件。 DPI…...

看不懂来打我!让性能提升56%的Vue3.5响应式重构
前言 在Vue3.5版本中最大的改动就是响应式重构,重构后性能竟然炸裂的提升了56%。之所以重构后的响应式性能提升幅度有这么大,主要还是归功于:双向链表和版本计数。这篇文章我们来讲讲使用双向链表后,Vue内部是如何实现依赖收集和…...

Halcon 极坐标变换
(1)极坐标的展开:polar_trans_image_ext(Image : PolarTransImage : Row, Column, AngleStart, AngleEnd, RadiusStart, RadiusEnd, Width, Height, Interpolation : ) (2)极坐标的逆变换:polar_trans_ima…...

JavaScript进阶--深入面向对象
深入面向对象 编程思想 面向过程:多个步骤> 解决问题 性能较高,适合跟硬件联系很紧密的东西,如单片机 但代码维护成本高,扩展性差 面向对象:问题所需功能分解为一个一个的对象(分工合作)>…...
Python列表专题:list与in
Python是一种强大的编程语言,其中列表(list)是最常用的数据结构之一。列表允许我们存储多个元素,并且可以方便地进行各种操作。在Python中,in运算符被广泛用于检测元素是否存在于列表中。本文将深入探讨Python列表及其与in运算符的结合使用。 1. Python列表的基础 1.1 什…...

利用Microsoft Entra Application Proxy在无公网IP条件下安全访问内网计算机
在现代混合办公环境中,如何让员工能够从任何地方安全访问公司内部资源成为了企业的重要挑战。传统的VPN解决方案虽然可以满足需求,但有时配置复杂,并可能涉及公网IP的问题。为了解决这个问题,Microsoft Entra(原Azure …...

【IEEE独立出版 | 厦门大学主办】第四届人工智能、机器人和通信国际会议(ICAIRC 2024)
【IEEE独立出版 | 厦门大学主办】 第四届人工智能、机器人和通信国际会议(ICAIRC 2024) 2024 4th International Conference on Artificial Intelligence, Robotics, and Communication 2024年12月27-29日 | 中国厦门 >>往届均已成功见刊检索…...
C++ 内存布局 - Part5: 继承关系中 构造析构与vptr的调整
这里以单继承为例,汇编采用AT&T格式,先看示例代码: #include <iostream>class Base { public:Base() {std::cout << "Base Constructor, this ptr: " << this << std::endl;printVptr();}virtual ~Ba…...
BUG-AttributeError: ‘EnforcedForest‘ object has no attribute ‘node‘
File “/home/adt/miniconda3/envs/bevdet/lib/python3.7/site-packages/trimesh/scene/transforms.py”, line 224, in nodes_geometry ‘geometry’ in self.transforms.node[n]): AttributeError: ‘EnforcedForest’ object has no attribute ‘node’ networkx 2.6.3 pyp…...
Spring Boot 3 配置 Redis 兼容单例和集群
配置项 Spring Boot 3.x 的 redis 配置和 Spring Boot 2.x 是不一样的, 路径多了一个data spring:...data:redis:host: redis.hostport: redis.portpassword: redis.passworddatabase: redis.database兼容单例和集群的配置 开发时一般用一个Redis单例就足够, 测试和生产环境…...
unsat钱包签名算法解析
unsat钱包签名算法解析 在数字货币领域,安全性是至关重要的,而签名算法则是确保交易和信息不可伪造的基础。本文将深入解析 unsat 钱包中使用的签名算法,重点关注如何生成和验证消息签名。 1. 签名算法概述 unsat 钱包使用 ECDSAÿ…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...