iOS端WebView场景使用HTTPDNS

重要

本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。

重要

本文档只针对WkWebView场景下如何使用HTTPDNS解析出的IP,关于HTTPDNS本身的解析服务,请先查看iOS SDK 开发手册

1. 前言

通过iOSNative场景使用HTTPDNS这篇文档,我们已经知道如何在iOS平台上的Native场景中如何使用HTTPDNS,实现防劫持、调度精准、解析及时生效的能力。

iOS上还有一个频繁发生网络请求的场景:WkWebView。WKWebViewWebKit框架提供的一个现代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官方统计数据(截至202564日),在系统版本分布上,iOS 17+已经占全部iPhone设备的85%以上,且在持续增长中。因此,考虑到HTTPDNSWkWebView场景带来的是防劫持、调度精准、解析及时生效等非功能性提升,建议直接通过本方案接入HTTPDNS,覆盖大部分客户,且旧版本系统用户,也会在后续的陆续版本升级中,逐渐享受这个能力。

3. 推荐方案:iOS 17+基于本地代理的方案

3.1 方案概述

iOS 17.0引入了ProxyConfiguration相关API,允许应用为WKWebView配置本地代理服务器。通过这种方式,我们可以在本地启动一个代理服务器,拦截WKWebView的所有网络请求,在代理层面实现HTTPDNS域名解析,然后将请求转发到真实服务器。

image

相比传统技术方案,本地代理方案具有以下显著优势:

  1. 稳定性:基于iOS 17+官方API,无需依赖私有API或混淆技术,稳定性和兼容性最佳。

  2. 适用性:对WebView完全透明,无需处理cookie、重定向、CORS等复杂细节。支持HTTP/HTTPS/WebSocket等所有协议,覆盖面更广。

  3. 安全性:本地代理独立在App沙箱内,并未向外界暴露,也无可利用的操作空间;

  4. 高性能:纯本地实现,所有网络数据只多了一次内存级拷贝,对于客户端来说是可以忽略不计的消耗;

  5. 易维护:实现逻辑清晰,维护成本相对较低。

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 APINetwork.framework,提供高性能的本地代理服务。详细的技术实现可参考开源代码:

GitHub源码地址: https://github.com/aliyun/alicloud-ios-sdk-emascurl/tree/master/EMASLocalProxy

核心技术要点:

  • 使用Network framework创建本地HTTP代理服务器

  • 通过CONNECT隧道处理HTTP请求

  • 实现客户端与目标服务器间的透明数据转发

  • 集成自定义DNS解析器支持HTTPDNS

  • 提供完善的降级和错误处理机制

3.4 降级及优化方案

EMASLocalHttpProxy在设计之初即充分考虑了多种异常场景,内建多层防护与自动降级(Fallback)机制,旨在提供高性能本地代理能力的同时,保障系统的高可用性与最终网络请求的可达性,即便在组件故障或外部依赖异常的情况下,也能最大程度减少对用户体验的影响。

以下是不同的异常场景及应对:

场景

触发条件

降级或保护措施

最终效果

代理启动

端口冲突

自动重试随机端口

提高启动成功率

启动阻塞

启动超时退出

避免应用卡死

代理运行

nw_listener 崩溃

标记服务为“未运行”

触发后续降级策略

外部访问尝试

仅监听 127.0.0.1

拒绝局域网访问,保障安全

WebView 配置

服务未运行

使用非持久化 WKWebsiteDataStore

回退到系统默认网络

系统版本低于iOS17

跳过代理配置

与系统默认行为保持一致

DNS 解析

自定义解析器抛异常

捕获异常,使用原始域名,走localDNS解析

避免因解析失败影响连接

网络连接

目标服务器连接失败

返回 502 Bad Gateway 响应

提供标准化故障反馈

通过以上多层设计,HttpdnsLocalHttpProxy 能够在复杂多变的网络环境中实现可靠运行。其完备的降级机制确保即使部分组件失效,仍能保障请求通路不中断。

4. 自定义WKURLSchemeHandler的方案

EMAS目前提供了一个实验性质的参考方案:EMASCurlWeb,该方案在Github上开源,可以考虑参考这个方案,自行结合业务需要,实现一个托管WkWebView网络请求的完整方案。在此基础上,还可以根据业务需要,进一步实现资源预取、自定义缓存等能力。

重要

此方案从使用界面来说提供了比较好的体验,无需额外考虑缓存、cookie、重定向等细节,但它内部实现复杂,需要接入时在一定程度上理解和改造这个方案来适配业务需要。

5. 全局拦截NSURLProtocol的方案

本方案是最早在iOS WebView上适配HTTPDNS的可行方案,它接入门槛实际上很高,效果也不好,只不过早年iOS并未提供其他选择,因此流传下来。它的原理是,基于NSURLProtocol可拦截iOS系统上基于上层网络库NSURLConnection/NSURLSession发出的网络请求,WkWebView发出的请求同样包含在内,拦截请求后,再使用HTTPDNS做域名解析和后续处理。步骤如下:

  1. 通过以下接口注册自定义NSURLProtocol,用于拦截WkWebView上层网络请求,并创建新的网络请求接管数据发送、接收、重定向等处理逻辑,将结果反馈给原始请求。

    [NSURLProtocol registerClass:[HttpDnsNSURLProtocolImpl class]];
  2. 自定义NSURLProtocol处理过程概述:

    • canInitWithRequest中过滤需要做HTTPDNS域名解析的请求。

    • 请求拦截后,做HTTPDNS域名解析。

    • 解析完成后,同普通请求一样,替换URL.host字段,替换HTTP Header Host域,并接管该请求的数据发送、接收、重定向等处理。

    • 通过NSURLProtocol的接口,将请求处理结果反馈到WebView原始请求。

  3. 自定义NSURLProtocol的实现逻辑可参考我们提供的demo中的 HttpDnsNSURLProtocolImpl.m

警告

由于Apple官方并未公开太多协议细节,上述方案在生产环境需要结合业务情况,自行处理cookie、重定向等细节问题,才具备可行性。非特殊情况,不建议使用此方案。

6. 方案总结与对比

本文档介绍了在iOSWKWebView场景下集成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带来的优势。

其他两种方案由于其固有的复杂性和不确定性,仅建议在有特殊需求且具备深厚技术储备的团队,经过充分评估和测试后谨慎采用。