非安全SSL证书HTTPS服务器的调用限制及iOS与浏览器访问差异问题
嘿,我来帮你理清这个问题的来龙去脉,还有对应的解决办法:
首先,为什么浏览器和NSURLSession的表现不一样?
浏览器对SSL证书的校验非常严格——只要证书是自签名、过期、域名不匹配这类非受信任的情况,就会直接弹出隐私错误,不让你轻易访问(除非你手动添加例外)。但iOS的NSURLSession呢?默认情况下,当遇到非安全证书时,它会触发服务器信任挑战(就是你看到的那条challenge : <NSURLProtectionSpace: 0x60000000e050>: Host:ucsmv2012.xx.xxxxx.com, Server:https, Auth-Scheme:NSURLAuthenticationMethodServerTrust, Realm:(null), Port:7002, Proxy:NO, Proxy-Type:(null)警告),但如果你的代码没有专门处理这个挑战,系统可能会默认“放行”,所以请求能返回200状态码,但那条警告就是在告诉你:“这个证书不安全,我其实是破例允许的”。
接下来,怎么实现你要的「限制Web服务调用」,也就是让NSURLSession拒绝非安全证书?
你需要通过NSURLSession的代理方法,手动处理服务器信任挑战,只接受合法的证书,拒绝非安全的。具体步骤如下:
1. 给NSURLSession设置代理
创建session的时候指定代理对象(记得让你的类遵守NSURLSessionDelegate协议):
// Swift示例 let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
// Objective-C示例 NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
2. 实现代理方法处理信任挑战
在你的代理类里实现didReceiveChallenge方法,严格校验证书:
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { // 只处理服务器信任类型的挑战 guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust else { completionHandler(.performDefaultHandling, nil) return } // 获取服务器信任对象 guard let serverTrust = challenge.protectionSpace.serverTrust else { // 无法获取信任对象,直接取消请求 completionHandler(.cancelAuthenticationChallenge, nil) return } // 用系统默认规则校验证书合法性 let isTrusted = SecTrustEvaluateWithError(serverTrust, nil) if isTrusted { // 证书合法,允许继续连接 let credential = URLCredential(trust: serverTrust) completionHandler(.useCredential, credential) } else { // 证书不合法,拒绝连接——这就实现了限制调用的需求 completionHandler(.cancelAuthenticationChallenge, nil) } }
Objective-C版本:
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; NSError *error = nil; BOOL isTrusted = SecTrustEvaluateWithError(serverTrust, &error); if (isTrusted) { NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential, credential); } else { // 拒绝非安全证书,取消请求 completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); } } else { completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); } }
额外提醒
- 如果你之前没实现这个代理方法,系统的默认行为在不同iOS版本可能有差异,有些版本会默认接受自签名证书,导致请求成功但留下警告。
- 当你拒绝非安全证书后,NSURLSession会取消请求,你可以在
URLSession:task:didCompleteWithError:代理方法里捕获这个错误,给用户提示“服务器证书不安全,无法访问”之类的信息。
内容的提问来源于stack exchange,提问作者Bappaditya

