iOS: HTTP Basic/ дайджест-аутентификация с UIWebView

обзор

Я работаю над решением для входа в SAML (единый вход, похожий на openID) для приложения iOS, которое включает показ контроллера представления с UIWebView и я сталкиваюсь с проблемой синхронизации и / или тайм-аута при обработке HTTP базовой / дайджест-аутентификации в UIWebView,

В частности, когда клиент получает запрос аутентификации HTTP, я выскакиваю UIAlertView запрашивая у пользователя идентификатор пользователя и пароль. Если пользователь может быстро ввести информацию (< 10 секунд), она работает. Однако, если запись занимает более 10 секунд, соединение, по-видимому, было разорвано и ничего не происходит.

Вопросы

  1. Есть ли тайм-аут на звонки connection:didReceiveAuthenticationChallenge: что помешало бы мне запрашивать у пользователя идентификатор пользователя и пароль (и ждать ввода пользователя)? У кого-нибудь есть обходной путь (например, какой-нибудь способ продлить время ожидания соединения)?
  2. Есть ли лучший способ обрабатывать основные HTTP / аутентификации от дайджеста UIWebView чем подкласс NSURLProtocol?

Детали и код

Для большинства систем SAML, которые мы должны обработать, логин будет отображаться как обычная веб-страница в UIWebView, Однако некоторые системы, с которыми нам нужно работать, прибегают к использованию базовой HTTP-аутентификации или дайджест-аутентификации HTTP для мобильных браузеров, поэтому мы должны быть в состоянии справиться и с этим.

Большие проблемы начинаются с того, что UIWebView не отображает сетевые вызовы внизу. Чтобы получить то, что мне нужно, я создал подкласс NSURLProtocol и зарегистрировал его, если необходимо:

[NSURLProtocol registerClass:[SMURLProtocol class]];

С этим, этот метод на SMURLProtocol вызывается при вызове HTTP basic/auth, поэтому я возвращаю YES, мы можем обработать HTTP basic & digest аутентификацию:

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    return ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]
        || [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]);
}

Теперь я сказал сетевому стеку, что SMURLProtocol может обработать запрос аутентификации, поэтому он вызывает

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
NSString *authenticationMethod = [protectionSpace authenticationMethod];

if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]
    || [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]) {
    // Stash the challenge in an IVAR so we can use it later
    _challenge = challenge;

    // These network operations are often on a background thread, so we have to make sure to be on the foreground thread
    // to interact with the UI. We tried the UIAlertView performSelectorOnMainThread, but ran into issues, so then
    // we switched to GCD with a semaphore?
    _dsema = dispatch_semaphore_create(0);
    dispatch_async(dispatch_get_main_queue(), ^{
        // Prompt the user to enter the userID and password
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"AUTHENTICATION_REQUIRED", @"")
                                                        message:[protectionSpace host]
                                                       delegate:self
                                              cancelButtonTitle:NSLocalizedString(@"CANCEL", @"")
                                              otherButtonTitles:NSLocalizedString(@"LOG_IN", @""), nil];
        [alert setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
        [alert show];
    });

    dispatch_semaphore_wait(_dsema, DISPATCH_TIME_FOREVER);

    // --> when you get here, the user has responded to the UIAlertView <--
    dispatch_release(_dsema);

}
}

Как вы можете видеть, я запускаю UIAlertView, чтобы запросить у пользователя идентификатор пользователя и пароль. Я должен сделать это обратно в главном потоке, потому что (очевидно, я не знаю наверняка) сетевой код работает в фоновом потоке. Я добавил семафор и явный код Grand Central Dispatch, чтобы обойти случайные сбои, которые я видел (основываясь на этой теме).

Последним элементом является делегат UIAlertView, который принимает идентификатор пользователя и пароль, который создает учетные данные для вызова:

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{ 
if (([alertView alertViewStyle] == UIAlertViewStyleLoginAndPasswordInput) && (buttonIndex == 1)) {
    NSString *userID = [[alertView textFieldAtIndex:0] text];
    NSString *password = [[alertView textFieldAtIndex:1] text];

    // when you get the reply that should unblock the background thread, unblock the other thread:
    dispatch_semaphore_signal(_dsema);

    // Use the userID and password entered by the user to proceed
    // with the authentication challenge.
    [_challenge.sender useCredential:[NSURLCredential credentialWithUser:userID
                                                                password:password
                                                             persistence:NSURLCredentialPersistenceNone]
          forAuthenticationChallenge:_challenge];
    [_challenge.sender continueWithoutCredentialForAuthenticationChallenge:_challenge];

    _challenge = nil;
}
}

Как я уже говорил в обзоре, все это прекрасно работает, если пользователь может ввести идентификатор пользователя и пароль менее чем за 10 секунд. Если это занимает больше времени, кажется, что время соединения истекло, и передача учетных данных отправителю запроса не имеет никакого эффекта.

0 ответов

Другие вопросы по тегам