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

Str*_*tch 49 iphone uiwebview ios

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

答案总是涉及:

  1. 使用私人api调用NSURLRequest:allowsAnyHTTPSCertificateForHost
  2. 使用NSURLConnection代替和委托canAuthenticateAgainstProtectionSpace

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

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

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

干杯,
拉伸.

Str*_*tch 76

终于我明白了!

你能做的是:

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

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

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

以下是相关的代码段.

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

(我使用实例var,_request因为在我的情况下,它是一个包含大量登录详细信息的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];
}
Run Code Online (Sandbox Code Playgroud)

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


Pro*_*gle 65

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];
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这有效,但有时它只是失败.这很奇怪.所有这些委托方法都被调用,就像它工作时一样,但我得到了"这个服务器的证书无效.你可能连接到假装是"mysite.com"的服务器,这可能会使你的机密信息面临风险. " 我使用了NSURLConnection` + sendAsynchronousRequest:queue:completionHandler:`方法. (4认同)
  • 您的代码baseURL中的拼写错误应为:[_ FailRequest URL] (2认同)

小智 16

这是灵丹妙药!


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.
}
Run Code Online (Sandbox Code Playgroud)

  • 你应该改变你的`NSURL*baseURL = [NSURL URLWithString:@"你的网址"];```NSURL*baseURL = [_FailedRequest URL];``willSendRequestForAuthenticationChallenge`中允许任何网址,而不仅仅是原始网址装了. (2认同)

zli*_*liw 7

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

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

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

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

如果您无权访问服务器证书,则可以回退到以下方法从任何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
Run Code Online (Sandbox Code Playgroud)

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

python -m SimpleHTTPServer 8000

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