2009-12-29 2 views
78

Мне нужно вызвать начальный GET HTTP request с базовым Authentication. Это будет первый раз, когда запрос будет отправлен на сервер, и у меня уже есть username & password, поэтому нет необходимости в вызове с сервера для авторизации.NSURLConnection и базовая HTTP-аутентификация в iOS

Первый вопрос:

  1. ли у NSURLConnection быть установлен как синхронное сделать Basic Auth? Согласно ответу на этот post, кажется, что вы не можете выполнять Basic Auth, если вы выбираете асинхронный маршрут.

  2. Кто-нибудь знает какой-либо пример кода, который иллюстрирует Basic Auth на GET request без необходимости ответа на вызов? Apple's documentation показывает пример, но только после того, как сервер выдал запрос на вызов клиенту.

Я как бы новая сетевая часть SDK, и я не уверен, какой из других классов я должен использовать, чтобы заставить эту работу работать. (Я вижу класс NSURLCredential, но кажется, что он используется только с NSURLAuthenticationChallenge после того, как клиент запросил у авторизованного ресурса с сервера).

ответ

127

Я использую асинхронное соединение с MGTwitterEngine и устанавливает разрешение на NSMutableURLRequest (theRequest) как так:

NSString *authStr = [NSString stringWithFormat:@"%@:%@", [self username], [self password]]; 
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding]; 
NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodingWithLineLength:80]]; 
[theRequest setValue:authValue forHTTPHeaderField:@"Authorization"]; 

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

+0

Должен отметить, что я делаю так, в OS X приложение, а не iPhone App, но, надеюсь, он все равно будет работать для вас – catsby

+0

Можете ли вы рассказать мне, в чем причина ограничения длины линии кодирования до 80 в вашем примере кода? Я думал, что заголовки HTTP имеют максимальную длину примерно 4k (или, возможно, некоторые серверы не занимают больше времени). –

+2

Я не писал эту часть, это всего лишь часть MGTwitterEngine, из категории, добавленной в NSData. См. NSData + Base64.h/m здесь: http://github.com/ctshryock/MGTwitterEngine – catsby

0

Можете ли вы рассказать мне, в чем причина ограничения длины линии кодирования до 80 в вашем примере кода? Я думал, что заголовки HTTP имеют максимальную длину примерно 4k (или, возможно, некоторые серверы не занимают больше времени). - Джастин Galzic 29 декабрь '09 в 17:29

Это не ограничивает до 80, это варианта метода base64EncodingWithLineLength в NSData + Base64.h/м, где вы можете разделить закодированную строку на несколько которые полезны для других приложений, таких как передача nntp. Я считаю, что 80 выбрано автором движка twitter как длина, достаточно большая, чтобы приспособить большинство закодированных пользователем/паролей результатов к одной строке.

79

Даже вопрос ответа, я хочу представить решение, которое не требует внешней LIBS, я нашел в другом потоке:

// Setup NSURLConnection 
NSURL *URL = [NSURL URLWithString:url]; 
NSURLRequest *request = [NSURLRequest requestWithURL:URL 
             cachePolicy:NSURLRequestUseProtocolCachePolicy 
            timeoutInterval:30.0]; 

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 
[connection start]; 
[connection release]; 

// NSURLConnection Delegates 
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 
    if ([challenge previousFailureCount] == 0) { 
     NSLog(@"received authentication challenge"); 
     NSURLCredential *newCredential = [NSURLCredential credentialWithUser:@"USER" 
                    password:@"PASSWORD" 
                   persistence:NSURLCredentialPersistenceForSession]; 
     NSLog(@"credential created"); 
     [[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge]; 
     NSLog(@"responded to authentication challenge");  
    } 
    else { 
     NSLog(@"previous authentication failure"); 
    } 
} 

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 
    ... 
} 

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 
    ... 
} 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 
    ... 
} 

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 
    ... 
} 
+7

Это не совсем то же самое, что и другие решения: это сначала связывается с сервером, получает ответ 401, а затем отвечает на правильные учетные данные. Значит, ты тратишь впустую. С другой стороны, ваш код будет обрабатывать другие проблемы, такие как HTTP Digest Auth. Это компромисс. – benzado

+0

Хорошая точка. Никогда не думал об этом. – dom

+2

Во всяком случае, это «правильный способ» сделать это. Все остальные способы - это ярлык. – lagos

7

Если вы не хотите импортировать весь MGTwitterEngine и вы не делаете асинхронный запрос Затем вы можете использовать http://www.chrisumbel.com/article/basic_authentication_iphone_cocoa_touch

Для base64 закодировать имя пользователя и пароль Так замените

NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodingWithLineLength:80]]; 

с

NSString *encodedLoginData = [Base64 encode:[loginString dataUsingEncoding:NSUTF8StringEncoding]]; 

после

вам нужно будет включать как следующий файл

static char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/"; 

@implementation Base64 
+(NSString *)encode:(NSData *)plainText { 
    int encodedLength = (((([plainText length] % 3) + [plainText length])/3) * 4) + 1; 
    unsigned char *outputBuffer = malloc(encodedLength); 
    unsigned char *inputBuffer = (unsigned char *)[plainText bytes]; 

    NSInteger i; 
    NSInteger j = 0; 
    int remain; 

    for(i = 0; i < [plainText length]; i += 3) { 
     remain = [plainText length] - i; 

     outputBuffer[j++] = alphabet[(inputBuffer[i] & 0xFC) >> 2]; 
     outputBuffer[j++] = alphabet[((inputBuffer[i] & 0x03) << 4) | 
            ((remain > 1) ? ((inputBuffer[i + 1] & 0xF0) >> 4): 0)]; 

     if(remain > 1) 
      outputBuffer[j++] = alphabet[((inputBuffer[i + 1] & 0x0F) << 2) 
             | ((remain > 2) ? ((inputBuffer[i + 2] & 0xC0) >> 6) : 0)]; 
     else 
      outputBuffer[j++] = '='; 

     if(remain > 2) 
      outputBuffer[j++] = alphabet[inputBuffer[i + 2] & 0x3F]; 
     else 
      outputBuffer[j++] = '=';    
    } 

    outputBuffer[j] = 0; 

    NSString *result = [NSString stringWithCString:outputBuffer length:strlen(outputBuffer)]; 
    free(outputBuffer); 

    return result; 
} 
@end 
1

Если вы используете GTMHTTPFetcher для подключения, базовая аутентификация довольно легко, как хорошо. Вам просто нужно предоставить учетные данные получателю перед началом выборки.

NSString * urlString = @"http://www.testurl.com/"; 
NSURL * url = [NSURL URLWithString:urlString]; 
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url]; 

NSURLCredential * credential = [NSURLCredential credentialWithUser:@"username" password:@"password" persistence:NSURLCredentialPersistenceForSession]; 

GTMHTTPFetcher * gFetcher = [GTMHTTPFetcher fetcherWithRequest:request]; 
gFetcher.credential = credential; 

[gFetcher beginFetchWithDelegate:self didFinishSelector:@selector(fetchCompleted:withData:andError:)]; 
10

Вот подробный ответ, без 3-й стороны, участвующей:

Пожалуйста, проверьте здесь:

//username and password value 
NSString *username = @“your_username”; 
NSString *password = @“your_password”; 

//HTTP Basic Authentication 
NSString *authenticationString = [NSString stringWithFormat:@"%@:%@", username, password]]; 
NSData *authenticationData = [authenticationString dataUsingEncoding:NSASCIIStringEncoding]; 
NSString *authenticationValue = [authenticationData base64Encoding]; 

//Set up your request 
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.your-api.com/“]]; 

// Set your user login credentials 
[request setValue:[NSString stringWithFormat:@"Basic %@", authenticationValue] forHTTPHeaderField:@"Authorization"]; 

// Send your request asynchronously 
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *responseCode, NSData *responseData, NSError *responseError) { 
     if ([responseData length] > 0 && responseError == nil){ 
      //logic here 
     }else if ([responseData length] == 0 && responseError == nil){ 
      NSLog(@"data error: %@", responseError); 
      UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"Error accessing the data" delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil]; 
      [alert show]; 
      [alert release]; 
     }else if (responseError != nil && responseError.code == NSURLErrorTimedOut){ 
      NSLog(@"data timeout: %@”, NSURLErrorTimedOut); 
      UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"connection timeout" delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil]; 
      [alert show]; 
      [alert release]; 
     }else if (responseError != nil){ 
      NSLog(@"data download error: %@”,responseError); 
      UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"data download error" delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil]; 
      [alert show]; 
      [alert release]; 
     } 
}] 

Пожалуйста, дайте мне знать ваше мнение по этому вопросу.

Благодаря

+0

очень хорошее спасибо :) – AmiiQo

+0

Метод base64Encoding используется для преобразования NSData в NSString теперь осуждается: '' '- (NSString *) base64Encoding NS_DEPRECATED (10_6, 10_9, 4_0, 7_0),' '' Лучше использовать Вместо NSDataBase64Encoding. – Ben

2

С NSData :: dataUsingEncoding осуждается (ИОС 7.0), вы можете использовать это решение:

// Forming string with credentials 'myusername:mypassword' 
NSString *authStr = [NSString stringWithFormat:@"%@:%@", username, password]; 
// Getting data from it 
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding]; 
// Encoding data with base64 and converting back to NSString 
NSString* authStrData = [[NSString alloc] initWithData:[authData base64EncodedDataWithOptions:NSDataBase64EncodingEndLineWithLineFeed] encoding:NSASCIIStringEncoding]; 
// Forming Basic Authorization string Header 
NSString *authValue = [NSString stringWithFormat:@"Basic %@", authStrData]; 
// Assigning it to request 
[request setValue:authValue forHTTPHeaderField:@"Authorization"]; 
Смежные вопросы