web-dev-qa-db-ja.com

UIWebViewで認証を適切に行う方法は?

UIWebViewでHTTP基本認証をサポートしたいのですが。

現在、私はリクエストをキャンセルしています

webView:shouldStartLoadWithRequest:navigationType:次に、自分のNSURLConnectionDelegateでそれらを処理して、必要に応じて資格情報を確認および提供します。次に、loadData:MIMEType:textEncodingName:baseURL:を使用して、WebビューにHTMLを表示します。これは、デリゲートに渡されるすべてのURLで正常に機能します。

私の問題は、デリゲートが画像、JavaScript、CSSファイルなどの埋め込み要素に対して呼び出されないことです。基本認証で保護された画像を参照するHTMLページがある場合、その画像を正しく読み込むことができません。さらに、Webビューがページを完全に読み込めなかったため、webView:didFinishLoad:が呼び出されることはありません。

App Storeで入手できるサードパーティのブラウザであるTerraでそのケースを確認したところ、その状況に完全に対処できます。私自身のNSURLProtocolを提供することでこれを解決することは可能だと思いますが、それは複雑すぎるようです。何が欠けていますか?

19
NeoNacho

認証が必要なすべてのドメインでsharedCredentialStorageを使用してみてください。

これは、Windowsに対してテストされたUIWebViewの作業サンプルですIIS BasicAuthenticationのみが有効になっています

これは、サイトの資格情報を追加する方法です。

NSString* login = @"MYDOMAIN\\myname";
NSURLCredential *credential = [NSURLCredential credentialWithUser:login
                                                         password:@"mypassword"
                                                      persistence:NSURLCredentialPersistenceForSession];

NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
                                         initWithHost:@"myhost"
                                                 port:80
                                             protocol:@"http"
                                                realm:@"myhost" // check your web site settigns or log messages of didReceiveAuthenticationChallenge
                                 authenticationMethod:NSURLAuthenticationMethodDefault];

[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential
                                                    forProtectionSpace:protectionSpace];
[protectionSpace release];

編集:Swift 4の同じコード

let login = "MYDOMAIN\\myname"
let credential = URLCredential(user:login, password:"mypassword", persistence:.forSession)
let protectionSpace = URLProtectionSpace(Host:"myhost", port:80, protocol:"http", realm:"myhost", authenticationMethod:NSURLAuthenticationMethodDefault)
URLCredentialStorage.shared.setDefaultCredential(credential, for:protectionSpace)

あなたのwebViewは今は機能するはずですが、機能しない場合は次のコードを使用してデバッグします。特に、didReceiveAuthenticationChallengeのログメッセージを確認します。

    #import "TheSplitAppDelegate.h"
    #import "RootViewController.h"

    @implementation TheSplitAppDelegate

    @synthesize window = _window;
    @synthesize splitViewController = _splitViewController;
    @synthesize rootViewController = _rootViewController;
    @synthesize detailViewController = _detailViewController;

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // Override point for customization after application launch.
        // Add the split view controller's view to the window and display.
        self.window.rootViewController = self.splitViewController;
        [self.window makeKeyAndVisible];

        NSLog(@"CONNECTION: Add credentials");

        NSString* login = @"MYDOMAIN\\myname";
        NSURLCredential *credential = [NSURLCredential credentialWithUser:login
                                                                 password:@"mypassword"
                                                              persistence:NSURLCredentialPersistenceForSession];

        NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
                                                 initWithHost:@"myhost"
                                                 port:80
                                                 protocol:@"http"
                                                 realm:@"myhost" // check your web site settigns or log messages of didReceiveAuthenticationChallenge
                                                 authenticationMethod:NSURLAuthenticationMethodDefault];


        [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];
        [protectionSpace release];    

        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://myhost/index.html"]
                                                               cachePolicy:NSURLRequestReloadIgnoringCacheData
                                                           timeoutInterval:12
                                        ];

        NSLog(@"CONNECTION: Run request");
        [[NSURLConnection alloc] initWithRequest:request delegate:self];

        return YES;
    }

    - (void)applicationWillResignActive:(UIApplication *)application
    {

    }

    - (void)applicationDidEnterBackground:(UIApplication *)application
    {

    }

    - (void)applicationWillEnterForeground:(UIApplication *)application
    {

    }

    - (void)applicationDidBecomeActive:(UIApplication *)application
    {

    }

    - (void)applicationWillTerminate:(UIApplication *)application
    {

    }

    - (void)dealloc
    {
        [_window release];
        [_splitViewController release];
        [_rootViewController release];
        [_detailViewController release];
        [super dealloc];
    }

    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
    {
        NSLog(@"CONNECTION: got auth challange");
        NSString* message = [NSString stringWithFormat:@"CONNECTION: cred cout = %i", [[[NSURLCredentialStorage sharedCredentialStorage] allCredentials] count]];
        NSLog(message);
        NSLog([connection description]);

        NSLog([NSString stringWithFormat:@"CONNECTION: Host = %@", [[challenge protectionSpace] Host]]);
        NSLog([NSString stringWithFormat:@"CONNECTION: port = %i", [[challenge protectionSpace] port]]);
        NSLog([NSString stringWithFormat:@"CONNECTION: protocol = %@", [[challenge protectionSpace] protocol]]);
        NSLog([NSString stringWithFormat:@"CONNECTION: realm = %@", [[challenge protectionSpace] realm]]);
        NSLog([NSString stringWithFormat:@"CONNECTION: authenticationMethod = %@", [[challenge protectionSpace] authenticationMethod]]);
    }

    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
        // release the connection, and the data object
        [connection release];

        // inform the user
        NSLog(@"CONNECTION: failed! Error - %@ %@",
              [error localizedDescription],
              [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
    } 

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

    - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
    {
        NSLog(@"CONNECTION: USE!");
        return YES;
    }


    @end

WebView認証の最終的なソリューションは、カスタムプロトコルの実装に基づいていました。すべてのプロトコルがスタックとして登録されているため、HTTPプロトコルを再定義すると、webViewからのすべてのリクエストがインターセプトされるため、着信リクエストに関連付けられている属性を確認し、新しいリクエストに再パックして、独自の接続を介して再度送信する必要があります。あなたはスタックしているので、あなたのリクエストはすぐにまたあなたに来て、あなたはそれを無視しなければなりません。したがって、プロトコルスタックは実際のHTTPプロトコル実装に移行します。リクエストが認証されていないため、認証リクエストが取得されます。認証後、サーバーから実際の応答が返されるので、応答を再パックして、webViewから受け取った元の要求に応答します。

新しいリクエストまたはレスポンスの本文を作成しようとしないでください。再送信する必要があります。最終的なコードは約30〜40行のコードであり、非常に単純ですが、多くのデバッグとテスティングが必要です。

残念ながら、ここではコードを提供できません。すでに別のプロジェクトに割り当てられているため、投稿が間違っていると言いたかったのですが、ユーザーがパスワードを変更するとスタックします。

Cocoaを使用したHTTP基本認証の秘密は、NSURLと関連クラスを知っていることです。

  • NSURL
  • NSURLRequest/NSMutableURLRequest
  • NSURLConnection
  • NSURLCredential
  • NSURLCredentialStorage
  • NSURLProtectionSpace
  • UIWebView/WebView/NIWebControllerなど.

本当の魔法はNSURLConnectionから来ています。 devDocsの言葉では、「NSURLConnectionオブジェクトは、URLリクエストのロードを実行するためのサポートを提供します。」 URLを表示せずにバックグラウンドでロードしたい場合は、NSURLConnectionを使用します。 NSURLConnectionの真の力はメソッドにあります

+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id < NSURLConnectionDelegate >)delegate

NSURLConnectionDelegateプロトコルには、正常な接続、致命的なエラー、および認証の課題に応答するためのメソッドがあります。 HTTP基本認証によって保護されたデータにアクセスしようとしている場合、これがCocoaが行う方法です。この時点で、例がいくらか明確になるはずです。

//basic HTTP authentication
NSURL *url = [NSURL URLWithString: urlString];
NSMutableURLRequest *request;
request = [NSMutableURLRequest requestWithURL:url
                              cachePolicy:NSURLRequestReloadIgnoringCacheData
                          timeoutInterval:12];
[self.webView openRequest:request];
(void)[NSURLConnection connectionWithRequest:request delegate:self];

これによりURLが作成されます。 URLからURLRequestが作成されます。次に、URLRequestがWebビューに読み込まれます。リクエストは、URLConnectionの作成にも使用されます。接続は実際には使用しませんが、認証に関する通知を受け取る必要があるため、デリゲートを設定します。デリゲートから必要なメソッドは2つだけです。

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{  
    NSURLCredential * cred = [NSURLCredential credentialWithUser:@"username"
                                                    password:@"password"
                                                 persistence:NSURLCredentialPersistenceForSession];
[[NSURLCredentialStorage sharedCredentialStorage]setCredential:cred forProtectionSpace:[challenge protectionSpace]];

}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
{
    return YES;
}

認証チャレンジがある場合は常に、資格情報が資格情報ストレージに追加されます。また、資格情報ストレージを使用するように接続に指示します。

8
Andrew Hoos

NSMutableURLRequestUIWebViewを使用して基本的な認証情報を設定することで、これを実装しました。これにより、sharedCredentialStorageの実装時に発生するラウンドトリップも回避されます(もちろん、トレードオフがあります)。

解決:

    NSString *url = @"http://www.my-url-which-requires-basic-auth.io"
    NSString *authStr = [NSString stringWithFormat:@"%@:%@", username, password];
    NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
    NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedString]];
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
    [mutableRequest setValue:authValue forHTTPHeaderField:@"Authorization"];
    NSURLRequest *request = [mutableRequest copy];
    NSURLRequest *request = [NSURLRequest basicAuthHTTPURLRequestForUrl:url];
    [self.webView loadRequest:request];

Matt Gallagherのページ からNSDataのbase64EncodedStringを実装するNSData + Base64カテゴリを取得できます(これは、ブログ投稿の下部にあります)

4
NSTJ

TKAURLProtocolProの場合[http://kadao.dir.bg/cocoa.htm] SVWebViewControllerの場合[https://github.com/samvermette/SVWebViewController]

2
Hla Min Swe

ログアウトは、セッションとUIWebView資格情報ではそれほど簡単ではないことを忘れないでください。ここで回答を参照してください: https://stackoverflow.com/a/18143902/2116338

0
mrplants