İOS'ta NSURLConnection ve Temel HTTP Kimlik Doğrulaması


85

GET HTTP requestBasic ile bir baş harf çağırmam gerekiyor Authentication. Bu, isteğin sunucuya ilk gönderimi olacak ve zaten bende var, username & passwordbu nedenle sunucudan yetkilendirme için bir sorgulamaya gerek yok.

İlk soru:

  1. Does NSURLConnectionTemel Auth yapmak senkron olarak ayarlanması gerekir? Bu gönderideki cevaba göre , asenkron rotayı tercih ederseniz Temel Kimlik Doğrulama yapamayacağınız görülüyor.

  2. Sınama GET requestyanıtına ihtiyaç duymadan Temel Kimlik Doğrulamasını gösteren herhangi bir örnek kod bilen var mı? Apple'ın belgeleri bir örnek gösterir, ancak yalnızca sunucu istemciye sorgulama talebini gönderdikten sonra.

SDK'nın ağ oluşturma kısmı konusunda biraz yeniyim ve bunu çalıştırmak için diğer sınıflardan hangisini kullanmam gerektiğinden emin değilim. ( NSURLCredentialSınıfı görüyorum ama öyle görünüyor ki NSURLAuthenticationChallenge, istemci sunucudan yetkili bir kaynak talep ettikten sonra kullanılıyor ).

Yanıtlar:


132

MGTwitterEngine ile asenkron bir bağlantı kullanıyorum ve yetkilendirmeyi NSMutableURLRequest( theRequest) içinde şu şekilde ayarlıyor :

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"];

Bu yöntemin meydan okuma döngüsünden geçmeyi gerektirdiğine inanmıyorum ama yanılıyor olabilirim


2
Bu kısmı ben yazmadım, sadece NSData'ya eklenen bir kategoriden MGTwitterEngine'in bir parçası. NSData + Base64.h / m'yi buradan görün: github.com/ctshryock/MGTwitterEngine
cats

7
Base64 kodlaması[authData base64EncodedString] için ( ), Matt Gallagher'dan NSData + Base64.h ve .m Dosyasını XCode-Project'inize ekleyin ( Mac ve iPhone'da Base64 kodlama seçenekleri ).
elim

3
NSASCIIStringEncoding, usascii olmayan kullanıcı adlarını veya parolaları bozacaktır. Bunun yerine NSUTF8StringEncoding kullanın
Dirk de Kok

4
base64EncodingWithLineLength, 2014 yılında NSData üzerinde mevcut değildir. Bunun yerine base64Encoding kullanın.
bickster

11
@bickster base64Encoding, iOS 7.0 ve OS X 10.9'dan beri kullanımdan kaldırılmıştır. Onun [authData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]yerine kullanıyorum. Ayrıca `NSDataBase64Encoding64CharacterLineLength` veyaNSDataBase64Encoding76CharacterLineLength
Dirk

80

Soru cevaplansa bile, başka bir başlıkta bulduğum dış kitaplara ihtiyaç duymayan çözümü sunmak istiyorum:

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

9
Bu, diğer çözümlerle tam olarak aynı değildir: bu, önce sunucuyla iletişim kurar, bir 401 yanıtı alır, ardından doğru kimlik bilgileriyle yanıt verir. Yani bir gidiş dönüşü boşa harcıyorsun. Tersine, kodunuz HTTP Özet Kimlik Doğrulama gibi diğer zorlukların üstesinden gelir. Bu bir değiş tokuş.
benzado

2
Her neyse, bunu yapmanın "doğru yolu" budur. Diğer tüm yollar bir kısayol.
lagos

1
Çok teşekkürler! @moosgummi
LE SANG

@dom Bunu kullandım ama nedense didRecieveAuthenticationChallenge çağrılmıyor ve siteden 403 erişim reddedildi mesajı alıyorum. Neyin yanlış gittiğini bilen var mı?
Declan McKenna

Evet, bunu yapmanın tek doğru yolu bu. Ve yalnızca ilk seferinde 401 yanıtına neden olur. Aynı sunucuya sonraki istekler kimlik doğrulama ile gönderilir.
dgatwood

12

Üçüncü tarafların dahil olmadığı ayrıntılı bir cevap:

Burada kontrol edin:

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

Lütfen bu konudaki geri bildiriminizi bana bildirin.

Teşekkürler


NSData'yı NSString'e dönüştürmek için kullandığınız base64Encoding yöntemi artık kullanımdan kaldırılmıştır: - (NSString *)base64Encoding NS_DEPRECATED(10_6, 10_9, 4_0, 7_0);Bunun yerine NSDataBase64Encoding kategorisini kullanmak daha iyidir.
Ben

7

MGTwitterEngine'in tamamını içe aktarmak istemiyorsanız ve eşzamansız bir istek yapmıyorsanız, http://www.chrisumbel.com/article/basic_authentication_iphone_cocoa_touch kullanabilirsiniz.

Base64 için Kullanıcı Adı ve parolayı kodlamak için değiştirin

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

ile

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

sonra

aşağıdaki dosyayı eklemeniz gerekecek

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

3

NSData :: dataUsingEncoding kullanımdan kaldırıldığından (ios 7.0), şu çözümü kullanabilirsiniz:

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

1

Bağlantınız için GTMHTTPFetcher kullanıyorsanız , temel kimlik doğrulaması da oldukça kolaydır. Getirme işlemine başlamadan önce alıcıya kimlik bilgilerini sağlamanız yeterlidir.

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:)];

0

Örnek kodunuzda kodlama satırı uzunluğunu 80 ile sınırlamanın arkasındaki nedenin ne olduğunu söyleyebilir misiniz? HTTP başlıklarının 4k gibi bir maksimum uzunluğa sahip olduğunu düşündüm (veya bazı sunucular bundan daha uzun sürmez). - Justin Galzic 29 Ara '09, 17:29

80 ile sınırlı değildir, NSData + Base64.h / m'deki base64EncodingWithLineLength yönteminin bir seçeneğidir, burada kodlanmış dizenizi birden çok satıra bölebilirsiniz; bu, nntp iletimi gibi diğer uygulamalar için yararlıdır. 80'in, twitter motoru yazarı tarafından çoğu kullanıcı / şifre kodlu sonucu bir satıra sığdıracak kadar büyük bir uzunluk olarak seçildiğine inanıyorum.


0

AFNetworking'i kullanabilirsiniz (açık kaynaklı), işte benim için çalışan kod. Bu kod, dosyayı temel kimlik doğrulamasıyla gönderir. Sadece url, e-posta ve şifreyi değiştirin.

NSString *serverUrl = [NSString stringWithFormat:@"http://www.yoursite.com/uploadlink", profile.host];
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:serverUrl parameters:nil error:nil];


NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

// Forming string with credentials 'myusername:mypassword'
NSString *authStr = [NSString stringWithFormat:@"%@:%@", email, emailPassword];
// 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"];

manager.responseSerializer = [AFHTTPResponseSerializer serializer];

NSURL *filePath = [NSURL fileURLWithPath:[url path]];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:^(NSProgress * _Nonnull uploadProgress) {
// This is not called back on the main queue.
// You are responsible for dispatching to the main queue for UI updates
     dispatch_async(dispatch_get_main_queue(), ^{
                //Update the progress view
                LLog(@"progres increase... %@ , fraction: %f", uploadProgress.debugDescription, uploadProgress.fractionCompleted);
            });
        } completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
            if (error) {
                NSLog(@"Error: %@", error);
            } else {
                NSLog(@"Success: %@ %@", response, responseObject);
            }
        }];
[uploadTask resume];
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.