it-roy-ru.com

NSURLConnection и базовая HTTP-аутентификация в iOS

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

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

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

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

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

83
Alexi Groove

Я использую асинхронное соединение с 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"];

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

130
catsby

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

// 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 {
    ...
}
80
dom

Вот подробный ответ без участия третьей стороны:

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

//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];
      }
}]

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

Спасибо

11
user3045072

Если вы не хотите импортировать весь 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 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

@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
7
Luke

Поскольку NSData :: dataUsingEncoding устарела (ios 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"];
3
Artem Zaytsev

Если вы используете 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:)];
1
John M. P. Knox

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

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

0
Gaius Parx