UIWebView 无法打开 HTTPS 安全 URL
结论
UIWebView 不会因为 URL 使用 HTTPS 就拒绝加载。更常见的原因是支付网关的 TLS/SSL 配置未通过 iOS 安全校验,比如证书链不完整、证书过期、域名不匹配,或者使用了旧版 TLS 和不兼容的加密套件。
Safari 能打开同一个 URL,不代表应用内的 WebView 也能正常加载。Safari 与应用使用的网络栈、缓存、Cookie、客户端证书及认证流程可能不同。排查时应先取得实际错误码,再根据错误修复服务器配置或应用的访问流程。
先记录真实错误
为 UIWebView 设置代理,记录加载失败信息:
@interface ViewController () <UIWebViewDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.webView.delegate = self;
NSString *urlString =
@"https://xxxseal.xxxxbank.com/mpi/Ssl.jsp?transactionId=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
}
- (void)webView:(UIWebView *)webView
didFailLoadWithError:(NSError *)error {
NSLog(@"WebView load failed");
NSLog(@"domain: %@", error.domain);
NSLog(@"code: %ld", (long)error.code);
NSLog(@"description: %@", error.localizedDescription);
NSLog(@"userInfo: %@", error.userInfo);
}
@end
不要看到页面空白就直接认定是证书问题。支付页面也可能因为重定向失败、Cookie 缺失、JavaScript 异常,或者服务器拒绝 WebView 的 User-Agent 而无法显示。
如果错误是 NSURLErrorCancelled,错误码通常为 -999,往往只是重定向或新请求替换旧请求时产生的取消通知,不一定表示加载失败。还要继续检查后续错误和服务器响应。
常见原因
1. 服务器没有返回完整证书链
支付网关可能只安装了站点证书,没有正确配置中间证书。桌面浏览器或 Safari 有时能通过缓存或其他机制补齐证书链,应用内网络请求则未必可以。
服务器应返回:
- 站点证书;
- 所需的中间 CA 证书;
- 顺序正确的证书链。
服务器通常不需要发送根 CA 证书。这个问题应由银行或支付网关的运维方修复,客户端不应靠跳过证书校验来掩盖配置错误。
2. 证书无法通过系统信任校验
需要检查:
- 证书是否已过期或尚未生效;
- 证书中的域名是否与请求域名一致;
- 证书是否由 iOS 信任的 CA 签发;
- 设备的日期和时间是否正确;
- 是否使用了自签名证书或企业内部 CA;
- 重定向涉及的其他 HTTPS 域名是否也有有效证书。
支付流程通常会跳转多个域名。入口 URL 的证书正常,不代表后续认证页、3-D Secure 页面和回调地址的证书也没有问题。
3. TLS 协议或加密套件过旧
旧支付系统可能只支持已经淘汰的 SSL/TLS 版本或旧加密算法。较老的 iOS 设备也可能不支持服务器要求的新协议和算法。
判断时需要结合以下信息:
- 出现问题的 iOS 版本;
- 服务器支持的 TLS 版本;
- 服务器提供的加密套件;
- 服务器是否要求 SNI;
- TLS 握手是否失败。
这类兼容问题通常需要在支付网关侧升级 TLS 配置。不要为了兼容旧服务器而降低支付应用的整体安全标准。
4. App Transport Security 限制
如果应用运行在已经引入 ATS 的 iOS 版本上,而服务器的 TLS 配置不符合 ATS 要求,请求可能会被阻止。不过,是否由 ATS 引起,仍要根据错误日志和实际系统版本判断,不能只看 URL 是否使用 HTTPS。
临时诊断时,可以为指定域名设置例外,但不建议对支付网关关闭全局安全限制:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>xxxxbank.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
是否还需要与 TLS 版本有关的例外键,要根据目标 iOS SDK、部署版本和服务器配置确定。ATS 例外无法修复证书过期、域名不匹配或证书不受信任等问题,也不适合作为支付环境中的长期方案。
5. 支付页面依赖 Cookie、POST 数据或请求头
有些支付网关不能通过复制最终 URL 直接进入。它们可能要求:
- 由商户服务器发起 POST 请求;
- 携带特定 Cookie 或 Session;
- 携带
Referer、User-Agent 等请求头; - 使用一次性且尚未过期的
transactionId; - 按规定顺序完成重定向;
- 通过自定义 URL Scheme 返回商户 App。
如果 Safari 是从完整支付流程中打开页面,而 UIWebView 只是单独加载最终 URL,那么两者实际上不是同一次请求。此时应严格按照支付网关提供的 iOS 接入文档构造请求,不能只加载地址字符串。
POST 表单请求可以按网关要求构造,例如:
NSURL *url = [NSURL URLWithString:@"https://xxxseal.xxxxbank.com/mpi/Ssl.jsp"];
NSString *bodyString =
@"transactionId=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
[request setValue:@"application/x-www-form-urlencoded"
forHTTPHeaderField:@"Content-Type"];
[self.webView loadRequest:request];
参数名称、编码方式、签名和 HTTP 方法必须以支付网关的实际接口规范为准。
推荐解决步骤
- 在
didFailLoadWithError:中记录完整的NSError。 - 确认失败发生在入口 URL,还是某个重定向地址。
- 在相同设备和网络环境下检查证书有效期、域名和完整证书链。
- 请网关维护方检查 TLS 协议、加密套件及中间证书配置。
- 核对支付接口是否要求 POST、Cookie、Session 或特定请求头。
- 如果日志明确指向 ATS,再添加范围最小的域名例外进行验证。
- 生产环境应优先修复服务器配置,不要绕过 TLS 信任检查。
新项目应改用 WKWebView
UIWebView 已不适合用于新项目。可以改用 WKWebView,它对导航、认证挑战和错误处理提供了更完整的支持:
#import <WebKit/WebKit.h>
@interface ViewController () <WKNavigationDelegate>
@property (nonatomic, strong) WKWebView *webView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
NSURL *url = [NSURL URLWithString:
@"https://xxxseal.xxxxbank.com/mpi/Ssl.jsp?transactionId=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"];
[self.webView loadRequest:[NSURLRequest requestWithURL:url]];
}
- (void)webView:(WKWebView *)webView
didFailProvisionalNavigation:(WKNavigation *)navigation
withError:(NSError *)error {
NSLog(@"Provisional navigation failed: %@", error);
}
- (void)webView:(WKWebView *)webView
didFailNavigation:(WKNavigation *)navigation
withError:(NSError *)error {
NSLog(@"Navigation failed: %@", error);
}
@end
即使改用 WKWebView,无效证书和错误的 TLS 配置仍需修复。更换 WebView 组件不能绕过 HTTPS 校验。
注意事项
不要在正式支付环境中接受所有服务器证书、忽略主机名校验,或无条件执行 NSURLSessionAuthChallengeUseCredential。这样会使应用容易受到中间人攻击,还可能导致交易信息泄露。
如果支付网关要求客户端证书、HTTP Basic/Digest 认证或特殊的 3-D Secure 交互,需要按照其接入规范处理认证挑战。仅凭当前 URL 无法判断是否存在这些要求,应以实际错误码、网络日志和网关文档为准。
备注:内容仅供参考。
