本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。
本文档只针对WkWebView场景下如何使用HTTPDNS解析出的IP,关于HTTPDNS本身的解析服务,请先查看iOS SDK 开发手册。
1. 前言
通过iOS端Native场景使用HTTPDNS这篇文档,我们已经知道如何在iOS平台上的Native场景中如何使用HTTPDNS,实现防劫持、调度精准、解析及时生效的能力。
而iOS上还有一个频繁发生网络请求的场景:WkWebView。WKWebView是WebKit框架提供的一个现代Web视图,用于在iOS应用中显示网页内容。它替代了旧的UIWebView,提供了更好的性能、更多的功能和更高的安全性。我们期望,在WkWebView加载网页的时候,也能使用HTTPDNS,提升网络安全性与网络性能。
2. 技术现状
随着iOS系统的不断发展,WKWebView集成HTTPDNS的技术方案也在持续演进:
iOS 17.0之前:Apple官方并未在WkWebView上开放DNS解析相关的hook接口,也并未直接开放自定义网络请求实现的接口,需要通过hook 私有API拦截流量的复杂方案实现。
iOS 17.0及以后:Apple引入了ProxyConfiguration相关API,为WKWebView提供了官方的代理配置能力,可以通过“接近完美”的方式拦截所有网络请求,使得优雅集成HTTPDNS成为可能。
根据Apple官方统计数据(截至2025年6月4日),在系统版本分布上,iOS 17+已经占全部iPhone设备的85%以上,且在持续增长中。因此,考虑到HTTPDNS为WkWebView场景带来的是防劫持、调度精准、解析及时生效等非功能性提升,建议直接通过本方案接入HTTPDNS,覆盖大部分客户,且旧版本系统用户,也会在后续的陆续版本升级中,逐渐享受这个能力。
3. 推荐方案:iOS 17+基于本地代理的方案
3.1 方案概述
iOS 17.0引入了ProxyConfiguration相关API,允许应用为WKWebView配置本地代理服务器。通过这种方式,我们可以在本地启动一个代理服务器,拦截WKWebView的所有网络请求,在代理层面实现HTTPDNS域名解析,然后将请求转发到真实服务器。
相比传统技术方案,本地代理方案具有以下显著优势:
稳定性:基于iOS 17+官方API,无需依赖私有API或混淆技术,稳定性和兼容性最佳。
适用性:对WebView完全透明,无需处理cookie、重定向、CORS等复杂细节。支持HTTP/HTTPS/WebSocket等所有协议,覆盖面更广。
安全性:本地代理独立在App沙箱内,并未向外界暴露,也无可利用的操作空间;
高性能:纯本地实现,所有网络数据只多了一次内存级拷贝,对于客户端来说是可以忽略不计的消耗;
易维护:实现逻辑清晰,维护成本相对较低。
3.2 接入参考
考虑创建本地代理服务、解析HTTP请求、基于HTTPDNS解析结果创建连接、数据转发等步骤有一定实现成本,我们在github上开源了一份实现:开源地址,并打包为SDK发布。您可以结合业务实际情况,按需调整这份实现,使得更符合您的业务需要。
3.2.1 Cocoapods集成
在Podfile中添加EMASLocalProxy依赖:
source 'https://github.com/aliyun/aliyun-specs.git'
target 'yourAppTarget' do
use_framework!
pod 'AlicloudHTTPDNS', 'x.x.x'
pod 'EMASLocalProxy', 'x.x.x'
end
3.2.2 使用示例
集成后,在WkWebView初始化时进行如下配置:
#import <EMASLocalProxy/EMASLocalProxy.h>
#import <AlicloudHttpDNS/AlicloudHttpDNS.h>
// 创建 WKWebViewConfiguration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
// 配置DNS解析器
[EMASLocalHttpProxy setDNSResolverBlock:^NSArray<NSString *> *(NSString *hostname) {
// 获取HTTPDNS服务实例
HttpDnsService *httpdns = [HttpDnsService sharedInstance];
HttpdnsResult *result = [httpdns resolveHostSyncNonBlocking:hostname byIpType:HttpdnsQueryIPTypeBoth];
if (result && (result.hasIpv4Address || result.hasIpv6Address)) {
NSMutableArray<NSString *> *allIPs = [NSMutableArray array];
if (result.hasIpv4Address) {
[allIPs addObjectsFromArray:result.ips];
}
if (result.hasIpv6Address) {
[allIPs addObjectsFromArray:result.ipv6s];
}
NSLog(@"HTTPDNS解析成功,域名: %@, IP: %@", hostname, allIPs);
return allIPs;
}
NSLog(@"HTTPDNS解析失败,域名: %@", hostname);
return nil;
}];
// 设置日志级别
[EMASLocalHttpProxy setLogLevel:EMASLocalHttpProxyLogLevelDebug];
// 配置WebView代理
BOOL proxyConfigured = [EMASLocalHttpProxy installIntoWkWebViewConfiguration:config];
if (proxyConfigured) {
NSLog(@"WebView代理配置成功");
} else {
NSLog(@"WebView代理配置失败,使用系统网络");
}
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
在上线前,您需要阅读和理解代码实现逻辑,并进行充分测试,确保完美兼容。
3.3 实现细节
EMASLocalProxy的完整实现基于iOS 17.0+的ProxyConfigurations API和Network.framework,提供高性能的本地代理服务。详细的技术实现可参考开源代码:
GitHub源码地址: https://github.com/aliyun/alicloud-ios-sdk-emascurl/tree/master/EMASLocalProxy
核心技术要点:
使用Network framework创建本地HTTP代理服务器
通过CONNECT隧道处理HTTP请求
实现客户端与目标服务器间的透明数据转发
集成自定义DNS解析器支持HTTPDNS
提供完善的降级和错误处理机制
3.4 降级及优化方案
EMASLocalHttpProxy在设计之初即充分考虑了多种异常场景,内建多层防护与自动降级(Fallback)机制,旨在提供高性能本地代理能力的同时,保障系统的高可用性与最终网络请求的可达性,即便在组件故障或外部依赖异常的情况下,也能最大程度减少对用户体验的影响。
以下是不同的异常场景及应对:
场景 | 触发条件 | 降级或保护措施 | 最终效果 |
代理启动 | 端口冲突 | 自动重试随机端口 | 提高启动成功率 |
启动阻塞 | 启动超时退出 | 避免应用卡死 | |
代理运行 |
| 标记服务为“未运行” | 触发后续降级策略 |
外部访问尝试 | 仅监听 127.0.0.1 | 拒绝局域网访问,保障安全 | |
WebView 配置 | 服务未运行 | 使用非持久化 | 回退到系统默认网络 |
系统版本低于iOS17 | 跳过代理配置 | 与系统默认行为保持一致 | |
DNS 解析 | 自定义解析器抛异常 | 捕获异常,使用原始域名,走localDNS解析 | 避免因解析失败影响连接 |
网络连接 | 目标服务器连接失败 | 返回 | 提供标准化故障反馈 |
通过以上多层设计,HttpdnsLocalHttpProxy
能够在复杂多变的网络环境中实现可靠运行。其完备的降级机制确保即使部分组件失效,仍能保障请求通路不中断。
4. 自定义WKURLSchemeHandler的方案
EMAS目前提供了一个实验性质的参考方案:EMASCurlWeb,该方案在Github上开源,可以考虑参考这个方案,自行结合业务需要,实现一个托管WkWebView网络请求的完整方案。在此基础上,还可以根据业务需要,进一步实现资源预取、自定义缓存等能力。
此方案从使用界面来说提供了比较好的体验,无需额外考虑缓存、cookie、重定向等细节,但它内部实现复杂,需要接入时在一定程度上理解和改造这个方案来适配业务需要。
5. 全局拦截NSURLProtocol的方案
本方案是最早在iOS WebView上适配HTTPDNS的可行方案,它接入门槛实际上很高,效果也不好,只不过早年iOS并未提供其他选择,因此流传下来。它的原理是,基于NSURLProtocol
可拦截iOS系统上基于上层网络库NSURLConnection/NSURLSession
发出的网络请求,WkWebView发出的请求同样包含在内,拦截请求后,再使用HTTPDNS做域名解析和后续处理。步骤如下:
通过以下接口注册自定义
NSURLProtocol
,用于拦截WkWebView上层网络请求,并创建新的网络请求接管数据发送、接收、重定向等处理逻辑,将结果反馈给原始请求。[NSURLProtocol registerClass:[HttpDnsNSURLProtocolImpl class]];
自定义
NSURLProtocol
处理过程概述:在
canInitWithRequest
中过滤需要做HTTPDNS域名解析的请求。请求拦截后,做HTTPDNS域名解析。
解析完成后,同普通请求一样,替换URL.host字段,替换HTTP Header Host域,并接管该请求的数据发送、接收、重定向等处理。
通过
NSURLProtocol
的接口,将请求处理结果反馈到WebView原始请求。
自定义
NSURLProtocol
的实现逻辑可参考我们提供的demo中的 HttpDnsNSURLProtocolImpl.m。
由于Apple官方并未公开太多协议细节,上述方案在生产环境需要结合业务情况,自行处理cookie、重定向等细节问题,才具备可行性。非特殊情况,不建议使用此方案。
6. 方案总结与对比
本文档介绍了在iOS端WKWebView
场景下集成HTTPDNS的三种主要技术方案:基于iOS 17+的本地代理方案、自定义WKURLSchemeHandler
方案以及全局拦截NSURLProtocol
的方案。
每种方案都有其特定的适用场景、优缺点和实现复杂度。为帮助您快速决策,下表对这三种方案进行了横向对比:
维度 / 方案 | 本地代理(ProxyConfiguration)(iOS 17 +) | 自定义 WKURLSchemeHandler(EMASCurlWeb 示例) | 全局 NSURLProtocol 拦截 |
官方支持度 | Apple 在 iOS 17 引入的正式 API,完全公开、长期可用 | 基于私有 WKURLSchemeHandler API,但需要自行实现完整网络栈 | 使用公开 NSURLProtocol API,但在 WKWebView 内部细节缺乏官方文档 |
生效版本 | iOS 17 及以上,iOS 17以下版本只是代理不生效,无其他副作用 | iOS 11 及以上(WKWebView 可用) | iOS 11 及以上 |
协议覆盖 | HTTP / HTTPS / WebSocket / HTTP2(透明转发原始流量) | 受限于自定义实现:HTTP / HTTPS(需自行扩展其他协议) | 仅限 NSURLSession / NSURLConnection 可处理的协议 |
实现复杂度 | 中:需实现本地代理、端口管理、双向转发 | 高:需重写请求流水线、缓存、Cookie、重定向等 | 中:需处理请求复写、线程与缓存交互 |
对业务代码侵入 | 低:WKWebView 只需配置代理 | 低:替换 WKWebView 的 scheme handler | 中:全局注册 NSURLProtocol,可能影响现有 NSURLSession 逻辑 |
Cookie / 缓存 / CORS | 代理层透明,不额外处理 | 由方案内部模拟,需自测完整性 | 开发者需自行维护,容易遗漏边缘场景 |
维护成本 | 低:依赖官方 API,版本升级风险小 | 高:社区示例,需长期跟进 WKWebView 变更 | 中:系统 API 稳定,但需关注WKWebView 内部行为变化 |
失效降级策略 | 支持:代理故障可回退系统网络 | 需自行实现 | 需自行实现 |
推荐场景 | 主推方案:目标用户 > iOS 17;对安全与兼容要求高 | 对旧系统版本仍需 HTTPDNS,且愿意投入较多研发资源 | 轻量改造、快速验证;对复杂请求兼容性要求不高 |
综合考虑稳定性、开发维护成本和未来趋势,我们强烈推荐使用基于iOS 17+的本地代理方案。
随着iOS 17+系统版本的高速普及,该方案能以最低的成本为绝大多数用户带来稳定、可靠的HTTPDNS服务,有效解决域名劫持问题,并提升网络性能。其优雅的实现方式和官方支持,确保了方案的长期可行性。对于低于iOS 17的系统版本,采取平滑降级策略,即直接使用系统默认的网络请求,待用户升级系统后自动享受HTTPDNS带来的优势。
其他两种方案由于其固有的复杂性和不确定性,仅建议在有特殊需求且具备深厚技术储备的团队,经过充分评估和测试后谨慎采用。