Могу ли я использовать 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, Вот как все обычно работает:

  1. NSURLConnection запрашивает страницу с сервера
  2. Сервер отвечает 401 ответом
  3. NSURLConnection смотрит, какие учетные данные он может почерпнуть из URL
  4. Если URL не предоставил полные учетные данные (имя пользователя и пароль), NSURLConnection также проконсультируем NSURLCredentialStorage заполнить пробелы
  5. Если полные учетные данные еще не определены, NSURLConnection отправит -connection:didReceiveAuthenticationChallenge: метод делегата, запрашивающий учетные данные
  6. Если 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, который я пишу, и он очень хорошо работает в моих тестах.

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