Не может повторно подтвердить подлинность с другими NSURLCredentials (используются старые удаленные) - PullRequest
16 голосов
/ 19 сентября 2011

Я искал stackoverflow, Google, Apple и другие места.Представленные советы выглядят многообещающе, я их реализовал, но в целом, похоже, не работает или не соблюдается.

Проблема: у меня есть NSURLConnection с конкретными учетными данными.Затем у меня есть выход, где я очищаю учетные данные, пространство защиты, я удаляю все кэшированные ответы и удаляю все куки в sharedHTTPCookieStorage, но при повторном вызове моего аутентифицированного запроса через несколько секунд, даже с неправильными учетными данными, я все еще использую старые (удалено) учетные данные

Вот некоторые выдержки кода, в которых удаляются учетные данные

        NSDictionary *credentialsDict = [[NSURLCredentialStorage sharedCredentialStorage] allCredentials];

    if ([credentialsDict count] > 0) {
        // the credentialsDict has NSURLProtectionSpace objs as keys and dicts of userName => NSURLCredential
        NSEnumerator *protectionSpaceEnumerator = [credentialsDict keyEnumerator];
        id urlProtectionSpace;

        // iterate over all NSURLProtectionSpaces
        while (urlProtectionSpace = [protectionSpaceEnumerator nextObject]) {
            NSEnumerator *userNameEnumerator = [[credentialsDict objectForKey:urlProtectionSpace] keyEnumerator];
            id userName;

            // iterate over all usernames for this protectionspace, which are the keys for the actual NSURLCredentials
            while (userName = [userNameEnumerator nextObject]) {
                NSURLCredential *cred = [[credentialsDict objectForKey:urlProtectionSpace] objectForKey:userName];
                WriteLog(@"Method: switchView removing credential %@",[cred user]);
                [[NSURLCredentialStorage sharedCredentialStorage] removeCredential:cred forProtectionSpace:urlProtectionSpace];
            }
        }
    }

Затем я удаляю все кэшированные ответы

    NSURLCache *sharedCache = [NSURLCache sharedURLCache];
    [sharedCache removeAllCachedResponses];

Затем удаляю все куки

    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    NSArray *cookies = [cookieStorage cookies];
    for (NSHTTPCookie *cookie in cookies) {
        [cookieStorage deleteCookie:cookie];
        NSLog(@"deleted cookie");
    }

Я также пытался не использовать куки и другие политики

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:theURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
[request setHTTPShouldHandleCookies:NO];
if(self.currentCookies != nil){
    [request setAllHTTPHeaderFields:
     [NSHTTPCookie requestHeaderFieldsWithCookies:nil]];
}

theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

Я также попробовал эту подсказку здесь, чтобы специально хранить куки и передавать их снова.http://www.hanspinckaers.com/multiple-nsurlrequests-with-different-cookies. В Интернете есть еще один блог, предлагающий добавить «#» к каждому URL для обеспечения повторной аутентификации, который работает, но просто не решает проблему, потому что мне нужно рассчитывать на учетные данные сессии и возможность использоватьсовершенно разные учетные данные.

Это ошибка или известная проблема, и как мне на самом деле ее решить ... Откровенно говоря: что я тут не так делаю?

Это действительно беспокоит меня иудерживаю меня от продолжения работы.

Я был бы очень признателен за любой вклад!

Большое спасибо!

Ответы [ 6 ]

7 голосов
/ 18 августа 2013

К сожалению, похоже, нет решения этой проблемы.

Вы можете использовать NSURLCredentialPersistenceNone или трюк #, или вы можете определить метод делегата connectionShouldUseCredentialStorage, чтобы возвратить NO. Если вы делаете это каждый раз, и ваше приложение никогда не сохраняет учетные данные для сеанса, это заставит вызов выполняться при каждом запросе.

Для приложений, выполняющих только минимальные запросы или использующих cookie-файл сеанса для проверки подлинности, это может работать нормально.

Для приложений, отправляющих большое количество запросов, все эти решения приводят к ответу 401 на каждый отдельный запрос, и этот дополнительный запрос-ответ может складываться с точки зрения данных и производительности.

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

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

Подробнее об этом можно прочитать по адресу: http://developer.apple.com/library/ios/qa/qa1727/_index.html

В этом документе упоминается ограниченный обходной путь, который включает добавление "." в конце имени сервера. Однако я не смог заставить это работать.

Кроме этого, я могу подумать о следующих решениях:

1) Всегда используйте обходной путь NSURLCredentialPersistenceNone & connectionShouldUseCredentialStorage, который должен генерировать 401. Добавьте заголовок Basic аутентификации в запрос самостоятельно. Это должно предотвратить лишние 401, а также обход хранилища учетных данных. Код для добавления этой авторизации выглядит примерно так:

    NSString *s ;
    NSString *authStr ;
    s = [NSString stringWithFormat:@"%@:%@",user,password] ;
    s = [YourBase64Encoder base64EncodingForData:[NSData dataWithBytes:[s UTF8String] length:strlen([s UTF8String])]];
    authStr = [NSString stringWithFormat:@"Basic %@",s] ;        
    [request setValue:authStr forHTTPHeaderField:@"Authorization"] ;

Я не знаю, как это будет реализовано для других методов аутентификации, но я предполагаю, что это возможно.

2) Сообщите пользователю о проблеме и попросите перезапустить приложение

3) Внедрите свой собственный низкоуровневый механизм поиска http на основе сокетов, который полностью обходит CFNetwork. Удачи в этом:>)

4 голосов
/ 13 января 2015

Я только что столкнулся с этой проблемой при AFNetworking. Я работаю с бэкэндом, который требует, чтобы авторизация была передана в шапке. Однако, когда пользователь выходит из приложения и пытается войти (даже с разными кредитами), я получаю сообщение об ошибке с сервера. Мое решение состояло в том, чтобы очистить cookie-файлы моих приложений при очистке authheader при выходе из системы.

- (void)clearAuthorizationHeader {
    [self.manager.requestSerializer clearAuthorizationHeader];
    NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in [storage cookies]) {
        [storage deleteCookie:cookie];
    }
}
3 голосов
/ 11 июля 2012

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

Я смог исправить это, передав NSURLCredentialPersistenceNone при инициализации моего NSURLCredential:

NSURLCredential* credentials = [[NSURLCredential alloc] initWithUser:username password:password persistence:NSURLCredentialPersistenceNone];

примечание: это вызовет 401 вызов на каждый HTTP-запрос, который вы делаете с этим NSURLCredential. Тем не менее, это не проблема, если вы вернете несколько файлов cookie, которые поддерживают вашу аутентификацию.

1 голос
/ 15 февраля 2013

Я знаю, что это старая тема. Однако единственное, что мне подходит, - это использование разных URL для разных сертификатов.

Это работало в моем приложении, поскольку у меня есть только 2 сертификата (один общий в ресурсах приложения и один пользовательский, загруженный из Интернета после процесса проверки пользователя). В моем случае нет файлов cookie и учетных данных, которые необходимо очистить, поэтому ни одно из решений, найденных мной в stackoverflow, не сработало.

1 голос
/ 06 октября 2011

Для чего это стоит, у меня та же проблема.

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

0 голосов
/ 09 сентября 2016

Я столкнулся с той же проблемой, теперь она работает.

Использование NSURLConnection эту проблему можно легко исправить, добавив случайное число в конец URL:

Итак, найдите ваш URLRequest и добавьте случайное число к URLRequest

NSInteger randomNumber = arc4random() % 999;
NSString *requestURL = [NSString stringWithFormat:@"%@?cache=%ld",yourURL,(long)randomNumber];

NSURL *URLRequest = [NSURL URLWithString:requestURL];

И убедитесь, что у вас есть случайное число в конце всех URL, которые вы звоните.

...