UIWebView查看自签名网站(没有私人API,不是NSURLConnection) - 有可能吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UIWebView查看自签名网站(没有私人API,不是NSURLConnection) - 有可能吗?相关的知识,希望对你有一定的参考价值。

有很多问题要问:我可以让UIWebView查看自签名的HTTPS网站吗?

答案总是涉及:

  1. 使用私人api调用NSURLRequestallowsAnyHTTPSCertificateForHost
  2. 使用NSURLConnection代替和代表canAuthenticateAgainstProtectionSpace

对我来说,这些都不行。 (1) - 表示我无法成功提交到应用商店。 (2) - 使用NSURLConnection意味着在加载初始html页面后必须从服务器获取的CSS,图像和其他内容。

有谁知道如何使用UIWebView查看自签名的https网页,这不涉及上述两种方法?

或者 - 如果使用NSURLConnection实际上可以用来渲染一个完整的CSS,图像和其他所有的网页 - 这将是伟大的!

干杯, 伸展。

答案

终于我明白了!

你能做的是:

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

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

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

以下是相关的代码段。

注意:您将看到的实例变量具有以下类型: UIWebView *_web NSURLConnection *_urlConnection NSURLRequest *_request

(我在_request中使用了一个实例var,因为在我的情况下,它是一个包含大量登录详细信息的POST,但如果需要,可以更改为使用传入的请求作为方法的参数。)

#pragma mark - Webview delegate

// Note: This method is particularly important. As the server is using a self signed certificate,
// we cannot use just UIWebView - as it doesn't allow for using self-certs. Instead, we stop the
// request in this method below, create an NSURLConnection (which can allow self-certs via the delegate methods
// which UIWebView does not have), authenticate using NSURLConnection, then use another UIWebView to complete
// the loading and viewing of the page. See connection:didReceiveAuthenticationChallenge to see how this works.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
{
    NSLog(@"Did start loading: %@ auth:%d", [[request URL] absoluteString], _authenticated);

    if (!_authenticated) {
        _authenticated = NO;

        _urlConnection = [[NSURLConnection alloc] initWithRequest:_request delegate:self];

        [_urlConnection start];

        return NO;
    }

    return YES;
}


#pragma mark - NURLConnection delegate

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
    NSLog(@"WebController Got auth challange via NSURLConnection");

    if ([challenge previousFailureCount] == 0)
    {
        _authenticated = YES;

        NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

        [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];

    } else
    {
        [[challenge sender] cancelAuthenticationChallenge:challenge];
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
{
    NSLog(@"WebController received response via NSURLConnection");

    // remake a webview call now that authentication has passed ok.
    _authenticated = YES;
    [_web loadRequest:_request];

    // Cancel the URL connection otherwise we double up (webview + url connection, same url = no good!)
    [_urlConnection cancel];
}

// We use this method is to accept an untrusted site which unfortunately we need to do, as our PVM servers are self signed.
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

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

另一答案

Stretch的答案似乎是一个很好的解决方法,但它使用了弃用的API。所以,我认为可能值得升级代码。

对于此代码示例,我将例程添加到包含我的UIWebView的ViewController。我使我的UIViewController成为一个UIWebViewDelegate和一个NSURLConnectionDataDelegate。然后我添加了2个数据成员:_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向同一站点发出多个请求。我没有尝试切换网站并试图回来。这可能导致需要重置_Authenticated。此外,如果要切换站点,则应为_Authenticated而不是BOOL保留字典(每个主机一个条目)。

另一答案

这是灵丹妙药!


BOOL _Authenticated;
NSURLRequest *_FailedRequest;

#pragma UIWebViewDelegate

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request   navigationType:(UIWebViewNavigationType)navigationType {
    BOOL result = _Authenticated;
    if (!_Authenticated) {
        _FailedRequest = request;
        NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
        [urlConnection start];
    }
    return result;
}

#pragma NSURLConnectionDelegate

-(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        NSURL* baseURL = [NSURL URLWithString:@"your 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];
    [self.webView loadRequest:_FailedRequest];
}

- (void)viewDidLoad{
   [super viewDidLoad];

    NSURL *url = [NSURL URLWithString:@"your url"];
    NSURLRequest *requestURL = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:requestURL];

// Do any additional setup after loading the view.
}
另一答案

如果要访问带有自签名证书的专用服务器以进行测试,则不必编写代码。您可以手动执行系统范围的证书导入。

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

这可以在以下情况下使用:

  • 测试设备的数量很少
  • 你信任服务器的证书

如果您无法访问服务器证书,则可以回退到following method以从任何HTTPS服务器中提取它(至少在Linux / Mac上,Windows人员必须在某处下载OpenSSL二进制文件):

echo "" | openssl s_client -connect $server:$port -prexit 2>/dev/null | sed -n -e '/BEGIN CERTIFICATE/,/END CERTIFICATE/ p' >server.pem

请注意,根据OpenSSL版本,证书可能会在文件中加倍,因此最好使用文本编辑器查看。将文件放在网络上的某个位置或使用

python -m SimpleHTTPServer 8000

通过http:// $ your_device_ip:8000 / server.pem从您的移动版Safari中访问它的快捷方式。

另一答案

这是一个聪明的解决方法。但是,可能更好(尽管代码更密集)的解决方案是使用NSURLProtocol,如Apple的CustomHTTPProtocol示例代码中所示。来自README:

“CustomHTTPProtocol显示了如何使用NSURLProtocol子类来拦截由高级子系统创建的NSURLC

以上是关于UIWebView查看自签名网站(没有私人API,不是NSURLConnection) - 有可能吗?的主要内容,如果未能解决你的问题,请参考以下文章

带有故事存档失败响应的 Instagram 私人 api 列表卷轴媒体查看器

如何添加自签名SSL证书 自签名SSL证书存风险

使用 Oracle APEX 对自签名、未经认证的网站进行 REST 调用

如何向自定义 UIWebview 添加“打开方式”功能?

UIWebView 和 NSURLRequest 上没有 Internet 连接处理 [重复]

UIWebView 内的自定义按钮(适用于 iPad)