51

これを尋ねる質問がたくさんあります:UIWebView自己署名されたHTTPS Webサイトを表示できますか?

そして、答えには常に次のいずれかが含まれます。

  1. プライベートAPI呼び出しを使用してNSURLRequestallowsAnyHTTPSCertificateForHost
  2. 代わりに使用NSURLConnectionし、デリゲートcanAuthenticateAgainstProtectionSpaceなど

私にとって、これらはしません。
(1)-アプリストアに正常に送信できないことを意味します。
(2)-NSURLConnectionを使用すると、最初のHTMLページを受信した後にサーバーからフェッチする必要のあるCSS、画像、およびその他のものが読み込まれません。

UIWebViewを使用して自己署名httpsWebページを表示する方法を知っている人はいますか?これには上記の2つの方法は含まれていません。

または-使用NSURLConnectionして実際にCSS、画像、その他すべてを備えたWebページをレンダリングできる場合-それは素晴らしいことです!

乾杯、
ストレッチ。

4

9 に答える 9

76

ついにできた!

あなたができることはこれです:

通常どおりにリクエストを開始しますUIWebView。次に、-in- NOwebView:shouldStartLoadWithRequestと応答し、代わりに同じ要求でNSURLConnectionを開始します。

を使用NSURLConnectionすると、自己署名サーバーと通信できます。これは、では使用できない追加のデリゲートメソッドを介して認証を制御できるためUIWebViewです。したがって、を使用connection:didReceiveAuthenticationChallengeして、自己署名サーバーに対して認証できます。

次に、でconnection:didReceiveData、リクエストをキャンセルし、 -をNSURLConnection使用して同じリクエストを再開しUIWebViewます。これは、サーバー認証をすでに通過しているため、これで機能します:)

以下に、関連するコードスニペットを示します。

注:表示されるインスタンス変数は次のタイプです。
UIWebView *_web
NSURLConnection *_urlConnection
NSURLRequest *_request

(私の場合のようにインスタンス変数を使用します。_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];
}

これが私が抱えていたのと同じ問題を抱えている他の人たちに役立つことを願っています!

于 2012-07-26T07:06:54.770 に答える
65

Stretchの答えは優れた回避策のようですが、非推奨のAPIを使用しています。だから、コードをアップグレードする価値があるのではないかと思いました。

このコードサンプルでは、​​UIWebViewを含むViewControllerにルーチンを追加しました。UIViewControllerをUIWebViewDelegateとNSURLConnectionDataDelegateにしました。次に、_Authenticatedと_FailedRequestの2つのデータメンバーを追加しました。これで、コードは次のようになります。

-(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をリセットする必要が生じる場合があります。また、サイトを切り替える場合は、BOOLではなく_Authenticatedの辞書(ホストごとに1つのエントリ)を保持する必要があります。

于 2013-02-25T18:59:13.797 に答える
17

これが万能薬です!


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.
}
于 2014-02-12T14:28:00.703 に答える
7

テストのためだけに自己署名証明書を使用してプライベートサーバーにアクセスする場合は、コードを記述する必要はありません。証明書のシステム全体のインポートを手動で行うことができます。

これを行うには、モバイルサファリでサーバー証明書をダウンロードする必要があります。これにより、インポートのプロンプトが表示されます。

これは、次の状況で使用できます。

  • テストデバイスの数が少ない
  • サーバーの証明書を信頼しています

サーバー証明書にアクセスできない場合は、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のバージョンによっては、ファイル内で証明書が2倍になる場合があるため、テキストエディタで確認することをお勧めします。ファイルをネットワーク上のどこかに置くか、

python -m SimpleHTTPServer 8000

http:// $ your_device_ip:8000/server.pemにあるモバイルサファリからアクセスするためのショートカット。

于 2014-10-18T10:14:48.420 に答える
4

これは賢い回避策です。ただし、おそらくより良い(コード集約的ではありますが)解決策は、AppleのCustomHTTPProtocolサンプルコードに示されているようにNSURLProtocolを使用することです。READMEから:

「CustomHTTPProtocolは、NSURLProtocolサブクラスを使用して、ネットワーク接続を公開しない高レベルのサブシステムによって作成されたNSURLConnectionsをインターセプトする方法を示しています。この特定のケースでは、Webビューによって作成されたHTTPSリクエストをインターセプトし、サーバーの信頼性評価をオーバーライドします。デフォルトで証明書が信頼されていないサイトを閲覧できるようにします。」

完全な例を確認してください: https ://developer.apple.com/library/ios/samplecode/CustomHTTPProtocol/Introduction/Intro.html

于 2013-12-02T19:07:24.223 に答える
3

これは私のために働く迅速な2.0互換の同等物です。私はこのコードをNSURLSessionの代わりに使用するように変換していません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!)
}
于 2015-10-08T01:18:32.750 に答える
2

ここにswift2.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)
                }
}
于 2016-01-07T07:27:46.667 に答える
1

@spirographerの回答を基に、Swift2.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はセッション内で行われていないようです。そのため、接続は拒否されます。私はにAllow Arbitrary Loads設定しましたInfo.plist。私を混乱させるのは、なぜNSURLConnection機能するのか(一見同じ考え)ですが、機能しないのNSURLSessionです。

于 2016-05-31T16:36:10.670 に答える
0

最初のものUIWebViewは非推奨です

WKWebView代わりに使用してください(iOS8から入手可能)

セットするwebView.navigationDelegate = self

埋め込む

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))
    }

}

そして、これを許可したいドメインのplistに追加します

<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>
于 2019-03-26T16:11:30.173 に答える