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下载服务器证书,然后提示导入 这在以下情况下可用:
- 测试设备的数量很少
- 您信任服务器的证书
在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>