Iphone UIWebView可查看自签名网站(无专用api,无NSURLConnection)-是否可能?

Iphone UIWebView可查看自签名网站(无专用api,无NSURLConnection)-是否可能?,iphone,ios,uiwebview,Iphone,Ios,Uiwebview,有一大堆问题问这个:我能让UIWebView查看一个自签名的HTTPS网站吗 答案总是包括以下两个方面: 对NSURLRequest使用私有api调用:allowanyHttpSCertificateforhost 改用NSURLConnection,委托canAuthenticateAgainstProtectionSpace等 对我来说,这些都不行。(1)-意味着我无法成功提交到应用商店。(2)-使用NSURLConnection意味着在收到初始HTML页面后必须从服务器获取的CSS、图像和

有一大堆问题问这个:我能让
UIWebView
查看一个自签名的HTTPS网站吗

答案总是包括以下两个方面:

  • NSURLRequest
    使用私有api调用:
    allowanyHttpSCertificateforhost
  • 改用
    NSURLConnection
    ,委托
    canAuthenticateAgainstProtectionSpace
  • 对我来说,这些都不行。
    (1)-意味着我无法成功提交到应用商店。
    (2)-使用NSURLConnection意味着在收到初始HTML页面后必须从服务器获取的CSS、图像和其他内容不会加载

    请问是否有人知道如何使用UIWebView查看不涉及上述两种方法的自签名https网页

    或者-如果使用
    NSURLConnection
    实际上可以用来呈现包含CSS、图像和其他所有内容的网页-那就太好了

    干杯,
    舒展筋骨。

    我终于得到了它

    你能做的是:

    正常使用
    UIWebView
    启动您的请求。然后-在
    webView:shouldStartLoadWithRequest
    -我们回复,并用相同的请求启动NSURLConnection

    使用
    NSURLConnection
    ,您可以与自签名服务器通信,因为我们能够通过额外的委托方法控制身份验证,而这些方法对
    UIWebView
    不可用。因此,使用
    connection:didReceiveAuthenticationChallenge
    我们可以对自签名服务器进行身份验证

    然后,在
    connection:didReceiveData
    中,我们取消
    NSURLConnection
    请求,并使用
    UIWebView
    再次启动相同的请求-现在可以工作了,因为我们已经通过了服务器身份验证:)

    下面是相关的代码片段

    注意:您将看到的实例变量类型如下:
    UIWebView*\u web

    NSURLConnection*\u urlConnection

    NSURLRequest*\u请求

    (我为
    \u request
    使用一个实例变量,就像在我的例子中一样,它是一篇包含大量登录详细信息的文章,但是如果需要,您可以更改为使用传入的请求作为方法的参数。)


    我希望这能帮助其他人解决与我同样的问题

    Stretch的答案似乎是一个很好的解决办法,但它使用了不推荐使用的API。因此,我认为它可能值得对代码进行升级

    对于这个代码示例,我将例程添加到包含UIWebView的ViewController中。我将UIViewController设置为UIWebViewDelegate和NSURLConnectionDataDelegate。然后我添加了两个数据成员:_Authenticated和_FailedRequest。因此,代码如下所示:

    -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
        BOOL result = _Authenticated;
        if (!_Authenticated) {
            _FailedRequest = request;
            [[NSURLConnection alloc] initWithRequest:request delegate:self];
        }
        return result;
    }
    
    -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            NSURL* baseURL = [_FailedRequest URL];
            if ([challenge.protectionSpace.host isEqualToString:baseURL.host]) {
                NSLog(@"trusting connection to host %@", challenge.protectionSpace.host);
                [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
            } else
                NSLog(@"Not trusting connection to host %@", challenge.protectionSpace.host);
        }
        [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
    }
    
    -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)pResponse {
        _Authenticated = YES;
        [connection cancel];
        [_WebView loadRequest:_FailedRequest];
    }
    

    加载视图时,我将_Authenticated设置为NO,并且不重置它。这似乎允许UIWebView向同一站点发出多个请求。我没有尝试切换网站,也没有尝试回来。这可能会导致需要重置_认证。此外,如果您要切换站点,您应该为“身份验证”保留一个字典(每个主机一个条目),而不是BOOL。

    这是一个巧妙的解决方法。然而,一个可能更好(尽管代码更密集)的解决方案是使用NSURLProtocol,如苹果的CustomHTTPProtocol示例代码所示。自述文件:

    “CustomHTTPProtocol演示了如何使用NSURLProtocol子类拦截高级子系统创建的NSURLConnections,该子系统不公开其网络连接。在这种特定情况下,它会拦截web视图发出的HTTPS请求,并覆盖服务器信任评估,从而允许您浏览其证书默认不受信任的站点。”

    查看完整示例: 这是灵丹妙药



    如果仅为了测试而想使用自签名证书访问专用服务器,则无需编写代码。您可以手动在系统范围内导入证书

    为此,需要使用mobile safari下载服务器证书,然后提示导入

    这在以下情况下可用:

    • 测试设备的数量很少
    • 您信任服务器的证书
    如果您没有访问服务器证书的权限,可以回退到从任何HTTPS服务器提取证书(至少在Linux/Mac上,windows人员必须在某处下载OpenSSL二进制文件):

    注意,根据OpenSSL版本的不同,文件中的证书可能会加倍,因此最好使用文本编辑器查看它

    python-msimplehttpserver 8000


    在http://$your_device_ip:8000/server.pem上从您的移动safari访问它的快捷方式。

    这是一个与swift 2.0兼容的等效软件,对我来说很有用。我没有将此代码转换为使用
    nsursession
    而不是
    NSURLConnection
    ,并且怀疑它会增加很多复杂性以正确使用它

    var authRequest : NSURLRequest? = nil
    var authenticated = false
    var trustedDomains = [:] // set up as necessary
    
    func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        if !authenticated {
            authRequest = request
            let urlConnection: NSURLConnection = NSURLConnection(request: request, delegate: self)!
            urlConnection.start()
            return false
        }
        else if isWebContent(request.URL!) { // write your method for this
            return true
        }
        return processData(request) // write your method for this
    }
    
    func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            let challengeHost = challenge.protectionSpace.host
            if let _ = trustedDomains[challengeHost] {
                challenge.sender!.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!), forAuthenticationChallenge: challenge)
            }
        }
        challenge.sender!.continueWithoutCredentialForAuthenticationChallenge(challenge)
    }
    
    func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
        authenticated = true
        connection.cancel()
        webview!.loadRequest(authRequest!)
    }
    

    这里是swift 2.0的工作代码

    var authRequest : NSURLRequest? = nil
    var authenticated = false
    
    
    func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
                    if !authenticated {
                        authRequest = request
                        let urlConnection: NSURLConnection = NSURLConnection(request: request, delegate: self)!
                        urlConnection.start()
                        return false
                    }
                    return true
    }
    
    func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
                    authenticated = true
                    connection.cancel()
                    webView!.loadRequest(authRequest!)
    }
    
    func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
    
                    let host = "www.example.com"
    
                    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust &&
                        challenge.protectionSpace.host == host {
                        let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
                        challenge.sender!.useCredential(credential, forAuthenticationChallenge: challenge)
                    } else {
                        challenge.sender!.performDefaultHandlingForAuthenticationChallenge!(challenge)
                    }
    }
    
    在此基础上,我为Swift 2.0用例与
    NSURLSession
    整合了一些东西。但是,这仍然不起作用。请参阅下面的更多信息

    func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        let result = _Authenticated
        if !result {
            let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
            let session = NSURLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
            let task = session.dataTaskWithRequest(request) {
                (data, response, error) -> Void in
                if error == nil {
                    if (!self._Authenticated) {
                        self._Authenticated = true;
                        let pageData = NSString(data: data!, encoding: NSUTF8StringEncoding)
                        self.webView.loadHTMLString(pageData as! String, baseURL: request.URL!)
    
                    } else {
                        self.webView.loadRequest(request)
                    }
                }
            }
            task.resume()
            return false
        }
        return result
    }
    
    func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
    }
    
    我将返回初始HTML响应,因此页面呈现普通HTML,但没有应用CSS样式(似乎获取CSS的请求被拒绝)。我看到了一系列错误:

    NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
    
    似乎使用
    webView发出的任何请求。loadRequest
    都不在会话中完成,这就是连接被拒绝的原因。我在
    Info.plist
    中设置了
    允许任意加载
    。让我困惑的是
    NSURLConnection
    为什么会起作用(似乎是相同的想法),但不是
    nsursession

    F
    var authRequest : NSURLRequest? = nil
    var authenticated = false
    
    
    func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
                    if !authenticated {
                        authRequest = request
                        let urlConnection: NSURLConnection = NSURLConnection(request: request, delegate: self)!
                        urlConnection.start()
                        return false
                    }
                    return true
    }
    
    func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
                    authenticated = true
                    connection.cancel()
                    webView!.loadRequest(authRequest!)
    }
    
    func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
    
                    let host = "www.example.com"
    
                    if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust &&
                        challenge.protectionSpace.host == host {
                        let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
                        challenge.sender!.useCredential(credential, forAuthenticationChallenge: challenge)
                    } else {
                        challenge.sender!.performDefaultHandlingForAuthenticationChallenge!(challenge)
                    }
    }
    
    func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        let result = _Authenticated
        if !result {
            let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
            let session = NSURLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
            let task = session.dataTaskWithRequest(request) {
                (data, response, error) -> Void in
                if error == nil {
                    if (!self._Authenticated) {
                        self._Authenticated = true;
                        let pageData = NSString(data: data!, encoding: NSUTF8StringEncoding)
                        self.webView.loadHTMLString(pageData as! String, baseURL: request.URL!)
    
                    } else {
                        self.webView.loadRequest(request)
                    }
                }
            }
            task.resume()
            return false
        }
        return result
    }
    
    func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
    }
    
    NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
    
    extension ViewController: WKNavigationDelegate {
    
    func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        let trust = challenge.protectionSpace.serverTrust!
        let exceptions = SecTrustCopyExceptions(trust)
        SecTrustSetExceptions(trust, exceptions)
            completionHandler(.useCredential, URLCredential(trust: trust))
        }
    
    }
    
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>localhost</key>
            <dict>
                <key>NSTemporaryExceptionAllowsInsecureHTTPSLoads</key>
                <false/>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSTemporaryExceptionMinimumTLSVersion</key>
                <string>1.0</string>
                <key>NSTemporaryExceptionRequiresForwardSecrecy</key>
                <false/>
            </dict>
        </dict>
    </dict>