2009-02-01 14 views
19

Tengo una clase Cocoa configurada que quiero usar para conectarme a un servicio web RESTful que estoy creando. He decidido utilizar la autenticación básica HTTP en mi backend PHP así ...¿Puedo usar NSURLCredentialStorage para autenticación básica HTTP?

<?php 
if (!isset($_SERVER['PHP_AUTH_USER'])) { 
    header('WWW-Authenticate: Basic realm="My Realm"'); 
    header('HTTP/1.0 401 Unauthorized'); 
    //Stuff that users will see if they click 'Cancel' 
    exit; 
} 
else { 
    //Validation Code 
    echo "You entered info."; 
} 
?> 

En este momento estoy usando un NSURLConnection sincrónica, que entiendo que los estados de documentación de Apple tiene menos soporte para la autenticación.

Pero, ¿es posible? Puedo hacer autenticación de cookies muy fácilmente sin NSURLProtectionSpaces o NSURLCredentials o cualquiera de las clases de autenticación. Además, ¿hay algún recurso donde pueda leer más sobre las clases de Autenticación Cocoa?

Gracias.

ACTUALIZACIÓN: mikeabdullahuk El código que ha proporcionado (el segundo ejemplo) es casi idéntico al que había escrito. He hecho un poco más de investigación, y descubrió que el NSURLConnection devuelve un error ...

Error Domain=NSURLErrorDomain Code=-1012 UserInfo=0x1a5170 "Operation could not be completed. (NSURLErrorDomain error -1012.)" 

El código corresponde a NSURLErrorUserCancelledAuthentication. Aparentemente, mi código no está accediendo a NSURLCredentialStorage y en su lugar está cancelando la autenticación. ¿Podría esto tener algo que ver con las funciones de Autenticación HTTP de PHP? Estoy bastante confundido en este punto.

Respuesta

62

Sincrónico NSURLConnection funcionará absolutamente con NSURLCredentialStorage. Así es como las cosas suelen trabajar:

  1. NSURLConnection solicita la página del servidor
  2. El servidor responde con una respuesta 401
  3. NSURLConnection mira para ver qué credenciales se puede deducir de la URL
  4. Si la URL hicieron no proporcionan credenciales completas (nombre de usuario y contraseña), NSURLConnection también consultará NSURLCredentialStorage para llenar los vacíos
  5. Si credenciales completas tienen aún no se ha determinado, NSURLConnection enviará el -connection:didReceiveAuthenticationChallenge: método delegado pidiendo credenciales
  6. Si el NSURLConnection ahora finalmente tiene credenciales completas, reintenta la solicitud original, incluidos los datos de autorización.

Al utilizar el método de conexión síncrono, única pierden en el paso 5, la capacidad de proporcionar autenticación personalizado. Por lo tanto, puede proporcionar previamente las credenciales de autenticación en la URL o colocarlas en NSURLCredentialStorage antes de enviar la solicitud. p.ej.

NSURLRequest *request = 
    [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://user:[email protected]"]]; 

[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL]; 

o:

NSURLCredential *credential = [NSURLCredential credentialWithUser:@"user" 
                 password:@"pass" 
                 persistence:NSURLCredentialPersistenceForSession]; 

NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] 
    initWithHost:@"example.com" 
    port:0 
    protocol:@"http" 
    realm:nil 
    authenticationMethod:nil]; 


[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential 
                forProtectionSpace:protectionSpace]; 
[protectionSpace release]; 

NSURLRequest *request = 
    [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com"]]; 

[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL]; 
+1

acabo se encontró con esto. Soy muy nuevo en ObjectiveC, pero me preguntaba si hubo que liberar algún objeto (específicamente credencial y protectionSpace). Por cierto, implementé este método en una llamada asincrónica (initWithRequest) y todavía obtuve la devolución de llamada didReceiveAuthenticationChallenge. – Bill

+0

@Bill, protectionSpace necesita ser liberado si se usa en el código de producción como utilicé + alloc. –

+0

protectionSpace tiene que ser lanzado. alloc devolverá un objeto con retainCount == 1 entonces después de [... setDefaultCredential: ...] deberías lanzarlo como [release de protectionSpace]; – sliver

0

quisiera señalar la respuesta de mikeabdullahuk es bueno, pero también si se utiliza NSURLCredentialPersistencePermanent en lugar de por sesión que va a almacenar las credenciales de los usuarios llavero así que la próxima vez que usted puede comprobar NSURLCredentialStorage de un valor no nulo para las credenciales predeterminadas para un espacio de protección y si obtienes un valor no nulo, puedes pasar las credenciales. Estoy usando este método ahora mismo para obtener un delicioso.Com Client estoy escribiendo y funciona muy bien en mis pruebas.

12

En una situación en la que un desafío de autenticación 401, o es inaceptable/imposible, a veces uso un CFHTTPMessage ficticia para generar la línea authetication, luego copia que de nuevo en el NSURLRequest:

// assume NSString *username and *password exist and NSURLRequest *urlRequest 
// exists and is fully configured except for HTTP Basic Authentication.. 

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

[urlRequest setValue:authorizationString forHTTPHeaderField:@"Authorization"]; 

Esto puede parecer completamente una forma extraña de hacerlo, pero es tolerante a situaciones donde el nombre de usuario/contraseña no son URL clean y donde NSURLRequest se niega a consultar NSURLCredentialStorage porque el servidor no está realmente enviando un HTTP 401 (por ejemplo, envía una página normal en su lugar))

+0

gracias por esto! es especialmente útil en situaciones donde el patrón delegado no es deseable. – kevboh

0

Indica tu credencial como la credencial por defecto para el protectionspace:

// Permananent, session, whatever. 
NSURLCredential *credential = [NSURLCredential credentialWithUser:username password:password persistence: NSURLCredentialPersistencePermanent]; 
// Make sure that if the server you are accessing presents a realm, you set it here. 
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:@"blah.com" port:0 protocol:@"http" realm:nil authenticationMethod:NSURLAuthenticationMethodHTTPBasic]; 
// Store it 
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace]; 

En este punto, cualquier NSURLConnection posterior que se impugna el uso de un espacio de protección que coincide con lo que establezca se utilice esta credencial

Cuestiones relacionadas