NSURLConnection и базовая HTTP-аутентификация в iOS
Мне нужно вызвать начальную GET HTTP request
с помощью Basic Authentication
. Это будет первый раз, когда запрос будет отправлен на сервер, и у меня уже есть username & password
, поэтому нет необходимости в вызове с сервера для авторизации.
Первый вопрос:
-
Нужно ли устанавливать NSURLConnection
как синхронно с Basic Auth? Согласно ответу на этот пост, кажется, что вы не можете выполнять Basic Auth, если вы выбираете асинхронный маршрут.
-
Кто-нибудь знает какой-нибудь пример кода, который иллюстрирует Basic Auth на GET request
без необходимости ответа на вызов? Документация Apple показывает пример, но только после того, как сервер выдал запрос на запрос клиенту.
Я как бы новая сетевая часть SDK, и я не уверен, какой из других классов я должен использовать, чтобы заставить это работать. (Я вижу класс NSURLCredential
, но кажется, что он используется только с NSURLAuthenticationChallenge
после того, как клиент запросил у авторизованного ресурса с сервера).
Ответы
Ответ 1
Я использую асинхронное соединение с 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"];
Я не верю, что этот метод требует прохождения цикла запроса, но я мог ошибаться
Ответ 2
Даже на вопрос ответ, я хочу представить решение, которое не требует внешних библиотек, я нашел в другом потоке:
// 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 {
...
}
Ответ 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];
}
}]
Пожалуйста, дайте мне знать ваши отзывы об этом.
Спасибо
Ответ 4
Если вы не хотите импортировать весь 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
Ответ 5
Так как 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"];
Ответ 6
Если вы используете 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:)];
Ответ 7
Можете ли вы рассказать мне, в чем причина ограничения длины линии кодирования до 80 в вашем примере кода? Я думал, что заголовки HTTP имеют максимальную длину примерно 4k (или, возможно, некоторые серверы не занимают больше времени). - Джастин Galzic Джастин Galzic
Это не ограничение до 80, это опция метода base64EncodingWithLineLength в NSData + Base64.h/m, где вы можете разделить закодированную строку на несколько строк, что полезно для другого приложения, например, для передачи nntp. Я считаю, что 80 выбран автором движка twitter как длина, достаточно большая, чтобы приспособить большинство пользовательских/паролированных результатов к одной строке.