Могу ли я использовать NSURLCredentialStorage для базовой аутентификации HTTP?
У меня настроен класс какао, который я хочу использовать для подключения к веб-сервису RESTful, который я создаю. Я решил использовать базовую аутентификацию HTTP на своем бэкэнде PHP следующим образом:
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
//Stuff that users will see if they click 'Cancel'
exit;
}
else {
//Validation Code
echo "You entered info.";
}
?>
На данный момент я использую синхронную NSURLConnection, которая, как я понимаю, в документации Apple утверждает, что аутентификация меньше поддерживается.
Но возможно ли это вообще? Я могу очень легко выполнить проверку подлинности с помощью файлов cookie без NSURLProtectionSpaces или NSURLCredentials или любого из классов проверки подлинности. Кроме того, есть ли ресурсы, где я могу прочитать больше о классах аутентификации Какао?
Благодарю.
ОБНОВЛЕНИЕ: mikeabdullahuk Код, который вы предоставили (второй пример), практически идентичен тому, что я написал. Я провел еще несколько расследований и обнаружил, что NSURLConnection возвращает ошибку...
Error Domain=NSURLErrorDomain Code=-1012 UserInfo=0x1a5170 "Operation could not be completed. (NSURLErrorDomain error -1012.)"
Код соответствует NSURLErrorUserCancelledAuthentication. Так что, очевидно, мой код не обращается к NSURLCredentialStorage, а вместо этого отменяет аутентификацию. Может ли это иметь какое-либо отношение к функциям PHP HTTP-аутентификации? Я довольно смущен в этом пункте.
4 ответа
Синхронный NSURLConnection
будет абсолютно работать с NSURLCredentialStorage
, Вот как все обычно работает:
NSURLConnection
запрашивает страницу с сервера- Сервер отвечает 401 ответом
NSURLConnection
смотрит, какие учетные данные он может почерпнуть из URL- Если URL не предоставил полные учетные данные (имя пользователя и пароль),
NSURLConnection
также проконсультируемNSURLCredentialStorage
заполнить пробелы - Если полные учетные данные еще не определены,
NSURLConnection
отправит-connection:didReceiveAuthenticationChallenge:
метод делегата, запрашивающий учетные данные - Если
NSURLConnection
теперь, наконец, имеет полные учетные данные, он повторяет исходный запрос, включая данные авторизации.
Используя метод синхронного подключения, вы теряете только на шаге 5 возможность обеспечить пользовательскую аутентификацию. Таким образом, вы можете предварительно предоставить учетные данные для аутентификации в URL-адресе или поместить их в NSURLCredentialStorage
перед отправкой запроса. например
NSURLRequest *request =
[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://user:pass@example.com"]];
[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
или же:
NSURLCredential *credential = [NSURLCredential credentialWithUser:@"user"
password:@"pass"
persistence:NSURLCredentialPersistenceForSession];
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost:@"example.com"
port:0
protocol:@"http"
realm:nil
authenticationMethod:nil];
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential
forProtectionSpace:protectionSpace];
[protectionSpace release];
NSURLRequest *request =
[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com"]];
[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
В ситуации, когда 401 или другая проверка подлинности неприемлема / невозможна, я иногда использую фиктивный CFHTTPMessage для генерации строки аутентификации, а затем копирую ее обратно в NSURLRequest:
// assume NSString *username and *password exist and NSURLRequest *urlRequest
// exists and is fully configured except for HTTP Basic Authentication..
CFHTTPMessageRef dummyRequest =
CFHTTPMessageCreateRequest(
kCFAllocatorDefault,
CFSTR("GET"),
(CFURLRef)[urlRequest URL],
kCFHTTPVersion1_1);
CFHTTPMessageAddAuthentication(
dummyRequest,
nil,
(CFStringRef)username,
(CFStringRef)password,
kCFHTTPAuthenticationSchemeBasic,
FALSE);
authorizationString =
(NSString *)CFHTTPMessageCopyHeaderFieldValue(
dummyRequest,
CFSTR("Authorization"));
CFRelease(dummyRequest);
[urlRequest setValue:authorizationString forHTTPHeaderField:@"Authorization"];
Это может показаться странным способом сделать это, но терпимо к ситуациям, когда имя пользователя / пароль не являются чистыми URL, и где NSURLRequest отказывается обращаться к NSURLCredentialStorage, потому что сервер фактически не отправляет HTTP 401 (например, он отправляет обычная страница вместо).
Установите свои учетные данные в качестве учетных данных по умолчанию для пространства защиты:
// Permananent, session, whatever.
NSURLCredential *credential = [NSURLCredential credentialWithUser:username password:password persistence: NSURLCredentialPersistencePermanent];
// Make sure that if the server you are accessing presents a realm, you set it here.
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:@"blah.com" port:0 protocol:@"http" realm:nil authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
// Store it
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];
На этом этапе любое последующее NSURLConnection, которое оспаривается с использованием пространства защиты, которое соответствует установленному вами, будет использовать эти учетные данные
Я хотел бы отметить, что ответ mikeabdullahuk хорош, но также если вы используете NSURLCredentialPersistencePermanent вместо сеанса, он сохранит учетные данные в цепочке ключей пользователей, поэтому в следующий раз вы можете проверить NSURLCredentialStorage для значения, отличного от nil, для учетных данных по умолчанию для пространства защиты и если вы получите ненулевое значение, в котором вы можете просто передать учетные данные. Я сейчас использую этот метод для клиента Delicious.com, который я пишу, и он очень хорошо работает в моих тестах.