Question

J'essaye de me connecter à http://cmis.demo.nuxeo. org / nuxeo / atom / cmis / avec NSURLConnection. Ce service Web de démonstration est documenté pour exiger une authentification (login: administrateur / mot de passe : Administrateur).

Modifier : ce service Web envoie maintenant un défi d'authentification, ce n'était pas au moment où la question a été posée.

Ce service Web n'envoie pas de défi d'authentification, je ne peux donc pas utiliser la méthode de délégation connection:didReceiveAuthenticationChallenge:. Au lieu de cela, j'ai défini un NSURLCredential par défaut dans le stockage d'informations d'identification partagé. Malheureusement, ces informations d'identification par défaut ne sont pas utilisées par NSURLConnection.

Voici mon code (utilisant ARC, testé sur iOS 5):

@implementation ViewController
{
    NSMutableData *responseData;
}

- (IBAction) connect:(id)sender
{
    NSString *user = @"Administrator";
    NSString *password = @"Administrator";
    NSURL *nuxeoURL = [NSURL URLWithString:@"http://cmis.demo.nuxeo.org/nuxeo/atom/cmis/"];

    NSURLCredential *credential = [NSURLCredential credentialWithUser:user password:password persistence:NSURLCredentialPersistenceForSession];
    NSString *host = [nuxeoURL host];
    NSNumber *port = [nuxeoURL port];
    NSString *protocol = [nuxeoURL scheme];
    NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:host port:[port integerValue] protocol:protocol realm:nil authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
    [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:nuxeoURL];
    BOOL manuallyAddAuthorizationHeader = NO;
    if (manuallyAddAuthorizationHeader)
    {
        NSData *authoritazion = [[NSString stringWithFormat:@"%@:%@", user, password] dataUsingEncoding:NSUTF8StringEncoding];
        NSString *basic = [NSString stringWithFormat:@"Basic %@", [authoritazion performSelector:@selector(base64Encoding)]];
        [request setValue:basic forHTTPHeaderField:@"Authorization"];
    }
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [connection start];
}

- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    responseData = [NSMutableData data];
}

- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [responseData appendData:data];
}

- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"connectionDidFinishLoading:%@", connection);
    NSLog(@"%@", [[NSString alloc] initWithData:responseData encoding:NSISOLatin1StringEncoding]);
}

- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"connection:%@ didFailWithError:%@", connection, error);
}

@end

Comme les informations d'identification par défaut ne sont pas utilisées, je reçois html (page de connexion) au lieu de xml. Si je règle la variable manuallyAddAuthorizationHeader sur YES, l'autorisation fonctionne et je reçois xml.

Ma question est la suivante: pourquoi NSURLConnection n'utilise pas automatiquement le NSURLCredential par défaut?

Était-ce utile?

La solution

L'envoi des informations d'identification par défaut sans défi d'authentification est considéré comme non sécurisé, en particulier dans le cas où vous communiquez via HTTP simple.La spécification HTTP indique que vous pouvez envoyer des informations d’identification de manière proactive, mais que vous devez généralement attendre un défi d’authentification.Et c'est le comportement que Cocoa fournit par défaut.

Si vous devez envoyer les informations de manière proactive, vous devrez ajouter manuellement les en-têtes vous-même.Vous pouvez simplement l'encoder en base 64 vous-même, ou vous pouvez utiliser les fonctions CFHTTP pour le faire pour vous, comme ceci:

CFHTTPMessageRef dummyRequest =
    CFHTTPMessageCreateRequest(
        kCFAllocatorDefault,
        CFSTR("GET"),
        (CFURLRef)[urlRequest URL],
        kCFHTTPVersion1_1);
CFHTTPMessageAddAuthentication(
    dummyRequest,
    nil,
    (CFStringRef)username,
    (CFStringRef)password,
    kCFHTTPAuthenticationSchemeBasic,
    FALSE);
authorizationString =
    (NSString *)CFHTTPMessageCopyHeaderFieldValue(
        dummyRequest,
        CFSTR("Authorization"));
CFRelease(dummyRequest);

Ensuite, définissez simplement le authorizationString comme en-tête Authorization dans votre NSURLRequest.

(Code copié de façon flagrante à partir de https://stackoverflow.com/a/509480/100478 , même si j'aij'ai écrit un code similaire plusieurs fois.)

Autres conseils

Comme HTTP prend en charge de nombreuses méthodes d'authentification différentes (Basic, Digest, Kerberos, etc.), NSURLConnection ne peut pas envoyer les informations d'identification avant de recevoir le défi d'authentification du serveur car il ne sait pas comment les envoyer.

Bien que la réponse de BJ puisse résoudre votre problème côté client, le vrai problème est le serveur. En interne, NSURLConnection utilisera son propre équivalent de la méthode de délégation connection:didReceiveAuthenticationChallenge: et des autres méthodes d'authentification.C'est là qu'il recherche un identifiant qu'il peut appliquer à partir de NSURLCredentialStorage.Dans votre cas, cela ne semble pas se produire.Pour que ces méthodes soient appelées, le serveur doit non seulement donner un code d'état HTTP 40x, mais il doit également inclure un en-tête WWW-Authenticate.Cela indique à NSURLConnection d'essayer de répondre au défi d'authentification.Il est probable que votre serveur n'inclut pas cela, et c'est pourquoi le client n'envoie pas d'informations d'identification.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top