NSURLConnection und grundlegende HTTP-Authentifizierung unter iOS

85

Ich muss eine Initiale GET HTTP requestmit Basic aufrufen Authentication. Dies wäre das erste Mal, dass die Anfrage an den Server gesendet wird, und ich habe bereits die, username & passwordsodass keine Anforderung vom Server zur Autorisierung erforderlich ist.

Erste Frage:

  1. Muss NSURLConnectionfür die Basisauthentifizierung synchron eingestellt werden? Laut der Antwort in diesem Beitrag können Sie Basic Auth anscheinend nicht ausführen, wenn Sie sich für die asynchrone Route entscheiden.

  2. Kennt jemand einen Beispielcode, der Basic Auth auf einem illustriert, GET requestohne dass eine Challenge-Antwort erforderlich ist? Die Dokumentation von Apple zeigt ein Beispiel, jedoch erst, nachdem der Server die Challenge-Anfrage an den Client gesendet hat.

Ich bin ein bisschen neu im Netzwerkbereich des SDK und ich bin mir nicht sicher, welche der anderen Klassen ich verwenden soll, um dies zum Laufen zu bringen. (Ich sehe die NSURLCredentialKlasse, aber es scheint, dass sie nur verwendet wird, NSURLAuthenticationChallengenachdem der Client eine autorisierte Ressource vom Server angefordert hat.)

Alexi Groove
quelle

Antworten:

132

Ich verwende eine asynchrone Verbindung mit MGTwitterEngine und die Berechtigung in NSMutableURLRequest( theRequest) wird wie folgt festgelegt :

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

Ich glaube nicht, dass diese Methode das Durchlaufen der Herausforderungsschleife erfordert, aber ich könnte mich irren

Katzenby
quelle
2
Ich habe diesen Teil nicht geschrieben, er ist nur ein Teil von MGTwitterEngine aus einer Kategorie, die zu NSData hinzugefügt wurde. Siehe NSData + Base64.h / m hier: github.com/ctshryock/MGTwitterEngine
Katzen vom
7
[authData base64EncodedString]Fügen Sie für base64-encoding ( ) die Dateien NSData + Base64.h und .m von Matt Gallagher zu Ihrem XCode-Projekt hinzu ( Base64-Codierungsoptionen auf Mac und iPhone ).
Elim
3
NSASCIIStringEncoding beschädigt Nicht-Usascii-Benutzernamen oder -Kennwörter. Verwenden Sie stattdessen NSUTF8StringEncoding
Dirk de Kok
4
base64EncodingWithLineLength ist im Jahr 2014 für NSData nicht vorhanden. Verwenden Sie stattdessen base64Encoding.
Bickster
11
@bickster base64Encodingist seit iOS 7.0 und OS X 10.9 veraltet. Ich benutze [authData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]stattdessen. Ebenfalls erhältlich sind `NSDataBase64Encoding64CharacterLineLength` oderNSDataBase64Encoding76CharacterLineLength
Dirk
80

Auch wenn die Frage beantwortet ist, möchte ich die Lösung vorstellen, für die keine externen Bibliotheken erforderlich sind, die ich in einem anderen Thread gefunden habe:

// 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 {
    ...
}
dom
quelle
9
Dies ist nicht ganz dasselbe wie bei den anderen Lösungen: Dies kontaktiert zuerst den Server, empfängt eine 401-Antwort und antwortet dann mit den richtigen Anmeldeinformationen. Sie verschwenden also eine Rundreise. Auf der anderen Seite wird Ihr Code andere Herausforderungen bewältigen, wie z. B. HTTP Digest Auth. Es ist ein Kompromiss.
Benzado
2
Auf jeden Fall ist dies der "richtige Weg", dies zu tun. Alle anderen Möglichkeiten sind eine Abkürzung.
Lagos
1
Vielen Dank! @ Moosgummi
LE SANG
@dom Ich habe dies verwendet, aber aus irgendeinem Grund wurde didRecieveAuthenticationChallenge nicht aufgerufen und ich erhalte eine Nachricht mit verweigertem Zugriff von der Site zurück. Weiß jemand, was falsch gelaufen ist?
Declan McKenna
Ja, dies ist der einzig richtige Weg, dies zu tun. Und es verursacht nur beim ersten Mal eine 401-Antwort. Nachfolgende Anforderungen an denselben Server werden mit Authentifizierung gesendet.
Dgatwood
12

Hier ist eine detaillierte Antwort ohne Beteiligung eines Dritten:

Bitte überprüfen Sie hier:

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

Bitte teilen Sie mir Ihr Feedback dazu mit.

Vielen Dank

user3045072
quelle
Die Methode base64Encoding, mit der Sie NSData in NSString konvertieren, ist jetzt veraltet: - (NSString *)base64Encoding NS_DEPRECATED(10_6, 10_9, 4_0, 7_0);Verwenden Sie stattdessen besser die Kategorie NSDataBase64Encoding.
Ben
7

Wenn Sie nicht die gesamte MGTwitterEngine importieren möchten und keine asynchrone Anforderung ausführen, können Sie http://www.chrisumbel.com/article/basic_authentication_iphone_cocoa_touch verwenden

Um base64 den Benutzernamen und das Passwort zu kodieren, ersetzen Sie diese

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

mit

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

nach dem

Sie müssen die folgende Datei einschließen

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
Luke
quelle
3

Da NSData :: dataUsingEncoding veraltet ist (ios 7.0), können Sie diese Lösung verwenden:

// 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"];
Artem Zaytsev
quelle
1

Wenn Sie GTMHTTPFetcher für Ihre Verbindung verwenden, ist die grundlegende Authentifizierung ebenfalls recht einfach. Sie müssen lediglich den Berechtigungsnachweis für den Abruf bereitstellen, bevor Sie mit dem Abrufen beginnen.

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:)];
John MP Knox
quelle
0

Können Sie mir sagen, warum in Ihrem Beispielcode die Länge der Codierungszeile auf 80 begrenzt wurde? Ich dachte, dass HTTP-Header eine maximale Länge von etwa 4 KB haben (oder dass einige Server nicht länger dauern). - Justin Galzic 29. Dezember 09 um 17:29

Es ist nicht auf 80 beschränkt, sondern eine Option der Methode base64EncodingWithLineLength in NSData + Base64.h / m, mit der Sie Ihre codierte Zeichenfolge in mehrere Zeilen aufteilen können, was für andere Anwendungen, z. B. die nntp-Übertragung, nützlich ist. Ich glaube, 80 wird vom Autor der Twitter-Engine als eine Länge ausgewählt, die groß genug ist, um die meisten benutzer- / passwortcodierten Ergebnisse in einer Zeile unterzubringen.

Gaius Parx
quelle
0

Sie können AFNetworking verwenden (es ist OpenSource). Hier ist Code, der für mich funktioniert hat. Dieser Code sendet eine Datei mit Basisauthentifizierung. Ändern Sie einfach URL, E-Mail und Passwort.

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];
Omanosoft
quelle