为了测试我的应用程序,我设置了一个带有 TLS 1.2 和自签名证书的服务器 Apache 2.4.12。问题是,当我运行我的应用程序并尝试与本地服务器通信时,出现此错误:
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
nil
Optional(Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo= {NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?...
这是因为自签名证书不可靠。我在 stackoverflow 的某个地方读到,在我使用 NSURLSession() 的类中,我必须添加以下委托:
func URLSession(session: NSURLSession,
task: NSURLSessionTask,
didReceiveChallenge challenge: NSURLAuthenticationChallenge,
completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?)
-> Void) {
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
}
然后执行:
func requestLoginWithURL (requestURL: NSURL, completionHandler: (success: Bool?) -> Void) {
let configuration =
NSURLSessionConfiguration.defaultSessionConfiguration()
let urlRequest: NSURLRequest = NSURLRequest(URL: requestURL)
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
/*
dataTaskWithRequest: creates an HTTP request for the specified URL request object, and calls a handler upon completion.
*/
let task = session.dataTaskWithRequest( ...)
}
但这似乎无论如何都行不通。所以我想知道是否有一种方法可以强制接受自分配证书,而无需将该 xml 代码添加到 info.plist 以禁用任何形式的安全通信。这是我服务器中的 httpd-ssl.conf:
<VirtualHost *:443>
DocumentRoot "/Applications/AMPPS/www"
ServerName localhost
SSLEngine on
SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCertificateFile "/Applications/AMPPS/apache/conf/server.crt"
SSLCertificateKeyFile "/Applications/AMPPS/apache/conf/server.key"
</VirtualHost>
有什么办法可以使这项工作?只是为了向我的老师展示我正在根据 iOS 9 ATS 构建我的应用程序。谢谢大家。
解决方案:
将此添加到文件 info.plist 中,这将强制 ATS 接受自签名证书:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<!--Include to allow subdomains-->
<key>NSIncludesSubdomains</key>
<true/>
<!--Include to allow HTTP requests-->
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<!--
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/> -->
</dict>
</dict>
</dict>
然而,正如这里解释的那样,这还不够。在您将使用 NSURLSession 的每个类中,您必须声明以下委托:
class LoginService: NSObject, NSURLSessionDelegate {
func URLSession(session: NSURLSession,
task: NSURLSessionTask,
didReceiveChallenge challenge: NSURLAuthenticationChallenge,
completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?)
-> Void) {
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
}
...
}
然后执行以下操作:
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let urlRequest: NSURLRequest = NSURLRequest(URL: requestURL)
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
/*
dataTaskWithRequest: creates an HTTP request for the specified URL request object, and calls a handler upon completion.
*/
let task = session.dataTaskWithRequest(urlRequest...)
这对我有用。请记住,您必须使用 Apache 2.4.x,因为它是唯一支持 TLS 1.2 的版本。希望这会有所帮助。