UIWebView查看自签名网站(没有私人API,不是NSURLConnection) - 有可能吗?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UIWebView查看自签名网站(没有私人API,不是NSURLConnection) - 有可能吗?相关的知识,希望对你有一定的参考价值。
有很多问题要问:我可以让UIWebView
查看自签名的HTTPS网站吗?
答案总是涉及:
- 使用私人api调用
NSURLRequest
:allowsAnyHTTPSCertificateForHost
- 使用
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 列表卷轴媒体查看器
使用 Oracle APEX 对自签名、未经认证的网站进行 REST 调用