Как выполнить проверку подлинности в UIWebView?

Я хотел бы поддержать базовую аутентификацию HTTP в своем UIWebView.

В настоящий момент я отменяю запросы в

webView:shouldStartLoadWithRequest:navigationType: затем обработайте их в моем собственном NSURLConnectionDelegate, чтобы проверить и предоставить учетные данные, если это необходимо. Затем я использую loadData:MIMEType:textEncodingName:baseURL: для представления HTML в веб-представлении. Это отлично подходит для любых URL-адресов, переданных делегату.

Моя проблема заключается в том, что делегат никогда не вызывается для внедренных элементов, таких как изображения, файлы JavaScript или CSS. Поэтому, если у меня есть HTML-страница, которая ссылается на изображение, которое защищено базовой аутентификацией, это изображение нельзя загрузить должным образом. Кроме того, webView:didFinishLoad: никогда не вызывается, потому что веб-представление не может полностью загрузить страницу.

Я проверил этот случай с Terra, сторонним браузером, доступным в App Store, и может полностью справиться с этой ситуацией. Я думаю, что можно было бы решить это, предоставив свой собственный NSURLProtocol, но это кажется слишком сложным. Что мне не хватает?

Ответы

Ответ 1

Попробуйте использовать sharedCredentialStorage для всех доменов, которые необходимы для аутентификации.

Вот рабочий пример для UIWebView, который был протестирован против Windows 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];    

Предполагается, что ваш веб-браузер будет работать сейчас, если он не работает, используйте следующий код для отладки, особенно проверьте сообщения журнала 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 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, и его.

Не пытайтесь создавать новые запросы или тела ответов, вам нужно просто отправить их повторно. Окончательный код был бы aproximetly 30-40 строк кода, и это довольно просто, но требует много отладки и tetsing.

К сожалению, я не могу предоставить код здесь, так как я назначен другому проекту уже, я просто хотел сказать, что мой пост неправильный, он запутывается, когда пользователь меняет пароль.

Ответ 2

Секрет базовой аутентификации HTTP с использованием cocoa - это знание 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 загружается в веб-представление. Запрос также используется для создания URLConnection. Мы действительно не используем соединение, но нам нужно получать уведомления об аутентификации, поэтому мы устанавливаем делегат. Нам нужно только два метода из делегата.

- (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;
}

Всякий раз, когда возникает запрос проверки подлинности, учетные данные добавляются в хранилище учетных данных. Вы также указываете подключение для использования хранилища учетных данных.

Ответ 3

Я только что реализовал это, установив базовые учетные данные с использованием NSMutableURLRequest для UIWebView. Это также позволяет избежать поездки туда и обратно при реализации 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];

Вы можете захватить категорию NSData + Base64, которая реализует base64EncodedString для NSData из страницы Мэтта Галлахера (это было в нижней части сообщение в блоге, когда я его загрузил)

Ответ 4

Для TKAURLProtocolPro [http://kadao.dir.bg/cocoa.htm] Для SVWebViewController [https://github.com/samvermette/SVWebViewController]

Ответ 5

Обязательно помните, что регистрация не так проста с сеансами и учетными данными UIWebView. См. Ответ здесь: fooobar.com/info/287191/....