Почему HTTPS NSURLSession соединение вызывается только один раз для каждого домена?
При подключении через HTTPS к серверу я реализую NSURLSessionDelegate
метод URLSession:didReceiveChallenge:completionHandler:
реализовать некоторые пользовательские функции.
Проблема в том, что этот метод делегата вызывается только при первом запросе (последующие запросы не вызывают этот метод). Моя пользовательская функциональность требует, чтобы метод делегата вызывался для каждого запроса.
Вот пример:
- (IBAction)reload:(id)sender {
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] delegate:self delegateQueue:nil];
// Note that https://www.example.com is not the site I'm really connecting to.
NSURL *URL = [NSURL URLWithString:@"https://www.example.com"];
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
[[session dataTaskWithRequest:URLRequest
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Response received here.
}] resume];
}
#pragma NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
// Called only for the first request, subsequent requests do no invoke this method.
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
Поскольку я хочу, чтобы URLCredential был для сеанса или для задачи, я проверил NSURLCredential
что я передаю completionHandler
и я обнаружил, что persistence
из NSURLCredentialPersistenceForSession
(который неизменен), который кажется правильным.
Я тоже проверил [NSURLCredentialStorage allCredentials]
и он пустой, поэтому он не кэширует учетные данные там.
Я заметил, что если впоследствии я сделаю запрос на URL-адрес HTTPS с другим доменом, вызов будет вызван для этого домена один раз, то есть для каждого домена.
Так почему же вызов сделан только один раз?
РЕДАКТИРОВАТЬ
Переключение на NSURLSessionTaskDelegate
и используя URLSession:task:didReceiveChallenge:completionHandler:
не имеет значения.
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
РЕДАКТИРОВАТЬ связанный вопрос
РЕДАКТИРОВАТЬ Так как в настоящее время, кажется, нет способа исправить это, я подал отчет об ошибке Apple: 19072802
1 ответ
То, что вы действительно пытаетесь сделать здесь, это оценить доверие учетных данных сервера для каждого запроса.
Техническое замечание 2232: Оценка доверия сервера HTTPS описывает оценку доверия HTTP на высоком уровне и более подробно описывает ее реализацию.
Когда вы подключаетесь к хосту с использованием SSL/TLS, хост представляет набор криптографических учетных данных. Ваше приложение (и, возможно, пользователь непосредственно) должно оценить эти учетные данные и решить, можно ли доверять.
Это похоже на просмотр чьих-либо водительских прав или паспорта и принятие решения о том, кем они являются.
Представьте, что вы просматривали идентификацию кого-то один раз для каждого произносимого слова. Это получило бы Тедиус! Это не имело бы смысла, если бы человек не изменился или не изменилась его идентификация. iOS выполнит оценку доверия, если сервер или его учетные данные изменятся.
Это на самом деле происходит на транспортном (сокетном) уровне под HTTP, но, к счастью, Foundation демонстрирует это выше в таких API, как NSURLConnection
а также NSURLSession
как учетная задача для данного пространства защиты. Если пространство защиты (хост) или учетные данные сервера изменяются, возникает новая проблема с учетными данными. Это, в свою очередь, вызовет оценку доверия.
Поскольку SSL / TLS - это мера безопасности на уровне сокетов, реальная работа происходит намного ниже базовой системы загрузки URL-адресов. SecureTransport
, структура защищенного сокета. SecureTransport
поддерживает собственный кэш сеанса TLS для каждого процесса. Это тот уровень, который вы должны обойти, чтобы получить поведение, которое вы ищете - вам нужно очистить кэш сеанса TLS для каждого соединения или принудительно SecureTransport
игнорировать кеш сессии для вашего процесса.
Технические вопросы и ответы 1727: кэш сеанса TLS описывает SecureTransport
кеширование сеанса более подробно, и может предоставить некоторые интересные варианты для обхода кеша TLS (то есть возиться с DNS).
В настоящее время нет API для очистки или изменения SecureTransport
Кеш сессии TLS. Вы можете подать радар, запрашивая эту функцию.
TL; DR;
"Так почему же вызов сделан только один раз?" Результат первой оценки доверия TLS кэшируется SecureTransport
в кеше сессии.
В настоящее время нет способа контролировать это конкретное поведение.
Вы можете попробовать использовать какую-нибудь другую HTTPS-библиотеку или инфраструктуру (такую как OpenSSL), YMMV.