Могу ли я использовать 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? Я очень запутался на этом этапе.


person mazniak    schedule 01.02.2009    source источник


Ответы (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:[email protected]"]];

[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];

or:

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];
person Mike Abdullah    schedule 02.02.2009
comment
Я только что наткнулся на это. Я очень новичок в ObjectiveC, но мне было интересно, нужно ли выпускать какие-либо объекты (в частности, учетные данные и protectionSpace). Кстати, я реализовал этот метод при асинхронном вызове (initWithRequest) и все равно получил обратный вызов didReceiveAuthenticationChallenge. - person Bill; 04.02.2009
comment
@Bill, protectionSpace действительно нуждается в освобождении, если используется в производственном коде, как я использовал + alloc. - person Mike Abdullah; 10.02.2009
comment
ProtectionSpace должен быть освобожден. alloc вернет объект с keepCount == 1, поэтому после [... setDefaultCredential: ...] вы должны освободить его как [protectionSpace release]; - person sliver; 09.11.2010
comment
NSURLCredentialStorage не сработал для меня, но просто префикс сервера в строке http с помощью user: pass действительно сработал, большое спасибо, супер простое решение. - person n13; 23.05.2012
comment
Я занимаюсь Objective-C и много занимаюсь веб-разработкой в ​​течение некоторого времени, и я никогда не знал, что вы можете ввести имя пользователя и пароль в URL-адресе, подобном этому +1. Ты узнаешь что-то новое каждый день. - person Popeye; 08.08.2012
comment
Согласно документации Apple для sendSynchronousRequest: «Если для загрузки запроса требуется аутентификация, необходимые учетные данные должны быть указаны как часть URL ...» Мой собственный опыт подтверждает это; то есть использование NSURLCRedentialStorage не работает с sendSynchronousRequest. - person Christopher King; 06.01.2013

В ситуации, когда 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 (например, он отправляет вместо этого обычная страница).

person Matt Gallagher    schedule 03.02.2009
comment
Спасибо за это! это особенно полезно в ситуациях, когда шаблон делегата нежелателен. - person kevboh; 23.12.2010

Я хотел бы отметить, что ответ mikeabdullahuk хорош, но также, если вы используете NSURLCredentialPersistencePermanent вместо сеанса, он будет хранить учетные данные в цепочке ключей пользователей, поэтому в следующий раз вы можете проверить NSURLCredentialStorage на ненулевое значение для учетных данных по умолчанию для пространства защиты, и если вы получите значение, отличное от нуля, вы можете просто передать учетные данные. Я использую этот метод прямо сейчас для клиента Delicious.com, который я пишу, и он очень хорошо работает в моих тестах.

person Colin Wheeler    schedule 02.02.2009

Установите свои учетные данные в качестве учетных данных по умолчанию для пространства защиты:

// 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, которое оспаривается с использованием пространства защиты, которое соответствует заданному вами, будет использовать эти учетные данные.

person quellish    schedule 13.02.2013