¿Cómo hacer la authentication correctamente en UIWebView?

Me gustaría admitir la authentication básica HTTP en mi UIWebView.

Por el momento, cancelo las requestes en

webView:shouldStartLoadWithRequest:navigationType: luego manéjelas en mi propio NSURLConnectionDelegate para verificar y proporcionar cnetworkingenciales si es necesario. Luego utilizo loadData:MIMEType:textEncodingName:baseURL: para presentar HTML en la vista web. Eso funciona bien para cualquier URL que se pase al delegado.

Mi problema es que el delegado nunca es llamado para elementos embeddeds, como imágenes, JavaScript o files CSS. Entonces, si tengo una página HTML que hace reference a una image que está protegida con authentication básica, esa image no se puede cargar correctamente. Además, webView:didFinishLoad: nunca se llama, porque la vista web no pudo cargar completamente la página.

He comprobado ese caso con Terra, un browser de terceros disponible en la App Store, y puede hacer frente a esa situación. Creo que sería posible resolver esto proporcionando mi propio protocolo NSURL, pero eso parece demasiado complicado. ¿Qué me estoy perdiendo?

Intente utilizar shanetworkingCnetworkingentialStorage para todos los dominios que necesita para autenticar.

Aquí hay una muestra de trabajo para UIWebView que se probó contra Windows IIS que solo tenía BasicAuthentication habilitada

Así es como agregar las cnetworkingenciales de su sitio:

  NSString* login = @"MYDOMAIN\\myname"; NSURLCnetworkingential *cnetworkingential = [NSURLCnetworkingential cnetworkingentialWithUser:login password:@"mypassword" persistence:NSURLCnetworkingentialPersistenceForSession]; NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:@"myhost" port:80 protocol:@"http" realm:@"myhost" // check your web site settigns or log messages of didReceiveAuthenticationChallenge authenticationMethod:NSURLAuthenticationMethodDefault]; [[NSURLCnetworkingentialStorage shanetworkingCnetworkingentialStorage] setDefaultCnetworkingential:cnetworkingential forProtectionSpace:protectionSpace]; [protectionSpace release]; 

Se supone que su webView funcione ahora, si no funciona, use el siguiente código para depurar, especialmente revise los posts de logging de didReceiveAuthenticationChallenge.

  #import "TheSplitAppDelegate.h" #import "RootViewController.h" @implementation TheSplitAppDelegate @synthesize window = _window; @synthesize splitViewController = _splitViewController; @synthesize rootViewController = _rootViewController; @synthesize detailViewController = _detailViewController; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. // Add the split view controller's view to the window and display. self.window.rootViewController = self.splitViewController; [self.window makeKeyAndVisible]; NSLog(@"CONNECTION: Add cnetworkingentials"); NSString* login = @"MYDOMAIN\\myname"; NSURLCnetworkingential *cnetworkingential = [NSURLCnetworkingential cnetworkingentialWithUser:login password:@"mypassword" persistence:NSURLCnetworkingentialPersistenceForSession]; NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:@"myhost" port:80 protocol:@"http" realm:@"myhost" // check your web site settigns or log messages of didReceiveAuthenticationChallenge authenticationMethod:NSURLAuthenticationMethodDefault]; [[NSURLCnetworkingentialStorage shanetworkingCnetworkingentialStorage] setDefaultCnetworkingential:cnetworkingential forProtectionSpace:protectionSpace]; [protectionSpace release]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://myhost/index.html"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:12 ]; NSLog(@"CONNECTION: Run request"); [[NSURLConnection alloc] initWithRequest:request delegate:self]; return YES; } - (void)applicationWillResignActive:(UIApplication *)application { } - (void)applicationDidEnterBackground:(UIApplication *)application { } - (void)applicationWillEnterForeground:(UIApplication *)application { } - (void)applicationDidBecomeActive:(UIApplication *)application { } - (void)applicationWillTerminate:(UIApplication *)application { } - (void)dealloc { [_window release]; [_splitViewController release]; [_rootViewController release]; [_detailViewController release]; [super dealloc]; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; { NSLog(@"CONNECTION: got auth challange"); NSString* message = [NSString stringWithFormat:@"CONNECTION: cnetworking cout = %i", [[[NSURLCnetworkingentialStorage shanetworkingCnetworkingentialStorage] allCnetworkingentials] count]]; NSLog(message); NSLog([connection description]); NSLog([NSString stringWithFormat:@"CONNECTION: host = %@", [[challenge protectionSpace] host]]); NSLog([NSString stringWithFormat:@"CONNECTION: port = %i", [[challenge protectionSpace] port]]); NSLog([NSString stringWithFormat:@"CONNECTION: protocol = %@", [[challenge protectionSpace] protocol]]); NSLog([NSString stringWithFormat:@"CONNECTION: realm = %@", [[challenge protectionSpace] realm]]); NSLog([NSString stringWithFormat:@"CONNECTION: authenticationMethod = %@", [[challenge protectionSpace] authenticationMethod]]); } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ // release the connection, and the data object [connection release]; // inform the user NSLog(@"CONNECTION: failed! Error - %@ %@", [error localizedDescription], [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]); } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; { NSLog(@"CONNECTION: received response via nsurlconnection"); } - (BOOL)connectionShouldUseCnetworkingentialStorage:(NSURLConnection *)connection; { NSLog(@"CONNECTION: USE!"); return YES; } @end 

La solución final para la authentication WebView se basó en la implementación del protocolo personalizado. Todos los protocolos se registran como una stack, por lo que si networkingefine el protocolo HTTP, interceptará todas las requestes procedentes de webView, por lo que deberá verificar los attributes asociados con la request entrante y volver a empaquetarla en una nueva request y enviarla nuevamente mediante su propia connection. Dado que está en astackmiento, su request se dirige de nuevo a usted y debe ignorarlo. Por lo tanto, se networkinguce la stack de protocolos a la implementación real del protocolo HTTP, ya que su request no está autenticada, obtendrá una request de authentication. Y después de authenticaiton obtendrá una respuesta real del server, por lo que volverá a comstackr la respuesta y responderá a la request original recibida de webView y eso es todo.

No intente crear nuevos cuerpos de requestes o respuestas, solo tiene que volver a enviarlos. El código final sería de aproximadamente 30-40 líneas de código y es bastante simple, pero requiere mucha debugging y teasing.

Lamentablemente, no puedo proporcionar el código aquí, ya que tengo asignado un proyecto diferente, solo quería decir que mi publicación es incorrecta, se bloquea cuando el usuario cambia la contraseña.

El secreto para la authentication básica de HTTP usando cocoa es conocer NSURL y las classs relacionadas.

  • NSURL
  • NSURLRequest / NSMutableURLRequest
  • NSURLConnection
  • NSURLCnetworkingential
  • NSURLCnetworkingentialStorage
  • NSURLProtectionSpace
  • UIWebView / WebView / NIWebController, etc.

La verdadera magia proviene de NSURLConnection. En palabras de devDocs, "Un object NSURLConnection proporciona soporte para realizar la carga de una request de URL". Si desea cargar alguna URL en el background sin mostrarla, usaría NSURLConnection. El verdadero poder de NSURLConnection está en el método

 + (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id < NSURLConnectionDelegate >)delegate 

El protocolo NSURLConnectionDelegate tiene methods para responder a conexiones exitosas, errores fatales y desafíos de authentication. Si está intentando acceder a los datos protegidos mediante authentication básica de HTTP, así es como Cocoa lo hace. En este punto, un ejemplo debería aportar algo de claridad.

 //basic HTTP authentication NSURL *url = [NSURL URLWithString: urlString]; NSMutableURLRequest *request; request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:12]; [self.webView openRequest:request]; (void)[NSURLConnection connectionWithRequest:request delegate:self]; 

Esto crea una URL. Desde la URL se crea URLRequest. La URLRequest se carga en la vista web. La request también se usa para crear una connection URLC. Realmente no usamos la connection, pero necesitamos recibir notifications sobre la authentication, así que configuramos el delegado. Solo hay dos methods que necesitamos del delegado.

 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; { NSURLCnetworkingential * cnetworking = [NSURLCnetworkingential cnetworkingentialWithUser:@"username" password:@"password" persistence:NSURLCnetworkingentialPersistenceForSession]; [[NSURLCnetworkingentialStorage shanetworkingCnetworkingentialStorage]setCnetworkingential:cnetworking forProtectionSpace:[challenge protectionSpace]]; } - (BOOL)connectionShouldUseCnetworkingentialStorage:(NSURLConnection *)connection; { return YES; } 

Cada vez que hay un desafío de authentication, se agrega una cnetworkingencial al almacenamiento de cnetworkingenciales. También le dice a la connection que use el almacenamiento de cnetworkingenciales.

Acabo de implementar esto configurando cnetworkingenciales de authentication básicas utilizando una NSMutableURLRequest para UIWebView . Esto también evita el viaje de ida y vuelta cuando se implementa shanetworkingCnetworkingentialStorage (por supuesto, hay intercambios involucrados).

Solución:

  NSString *url = @"http://www.my-url-which-requires-basic-auth.io" NSString *authStr = [NSString stringWithFormat:@"%@:%@", username, password]; NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding]; NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedString]]; NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]]; [mutableRequest setValue:authValue forHTTPHeaderField:@"Authorization"]; NSURLRequest *request = [mutableRequest copy]; NSURLRequest *request = [NSURLRequest basicAuthHTTPURLRequestForUrl:url]; [self.webView loadRequest:request]; 

Puede get la categoría NSData + Base64 que implementa la base64EncodedString para NSData desde la página de Matt Gallagher (estaba en la parte inferior de la publicación del blog cuando la descargué)

Para TKAURLProtocolPro [http://kadao.dir.bg/cocoa.htm] Para SVWebViewController [https://github.com/samvermette/SVWebViewController]

Asegúrese de recordar que cerrar session no es tan fácil con las sesiones y cnetworkingenciales de UIWebView. Vea la respuesta aquí: https://stackoverflow.com/a/18143902/2116338 .