Problema de inicio de session de authentication RESTful iOS

Hey Folks, tengo un problema con la authentication de las cnetworkingenciales de inicio de session de mi aplicación. Actualmente tengo un service web de Rails que se ejecuta desde mi server local, que count con una pantalla de inicio de session principal para un usuario administrador con cnetworkingenciales de contraseña para mostrar funciones.

El problema es que estoy creando una aplicación RESTful iOS que se ejecuta junto con mi aplicación de Rails, que solo toma estas requestes JSON y las omite.

Todo lo que quiero es crear una forma para que ingrese el administrador y la contraseña sin tener que usar auth_token? parece ser la única forma de hacerlo, simplemente autenticar al usuario administrador primero en el llavero. Estoy usando el marco AFNetowrking para la authentication. SSKeychain un contenedor para counts, y SVProgressHUD para los huds ligeros

. También tengo las requestes JSON y XML que se registran en el terminal i pero fallan debido a que no pueden conectarse al server con este error

error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server." UserInfo=0x7556d50 {NSErrorFailingURLStringKey=http://localhost:3000.json/, NSErrorFailingURLKey=http://localhost:3000.json/, NSLocalizedDescription=Could not connect to the server., NSUnderlyingError=0xeb805c0 "Could not connect to the server."} 

así es como estoy almacenando las cnetworkingenciales para el authClient, todo lo que quiero hacer es especificar la misma información utilizada para iniciar session en el cliente del service web.

nombre de usuario: administrador y contraseña: taliesin

¿Pero no sé cómo hacerlo?

esto es lo que tengo para el AuthAPIClient, CnetworkingentialsStore y LoginViewController

actualice si alguien sabe una forma más fácil de hacer esto, por favor, me puede informar, será muy apreciado.

AuthAPIClient.m

 #import "AuthAPIClient.h" #import "CnetworkingentialStore.h" #define BASE_URL @"http://admin:taliesin@localhost:3000" @implementation AuthAPIClient + (id)shanetworkingClient { static AuthAPIClient *__instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSURL *baseUrl = [NSURL URLWithString:BASE_URL]; __instance = [[AuthAPIClient alloc] initWithBaseURL:baseUrl]; }); return __instance; } - (id)initWithBaseURL:(NSURL *)url { self = [super initWithBaseURL:url]; if (self) { [self registerHTTPOperationClass:[AFJSONRequestOperation class]]; [self setAuthTokenHeader]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tokenChanged:) name:@"token-changed" object:nil]; } return self; } - (void)setAuthTokenHeader { CnetworkingentialStore *store = [[CnetworkingentialStore alloc] init]; NSString *authToken = [store authToken]; [self setDefaultHeader:@"auth_token" value:authToken]; } - (void)tokenChanged:(NSNotification *)notification { [self setAuthTokenHeader]; } @end 

CnetworkingentialStore.m

 #import "CnetworkingentialStore.h" #import "SSKeychain.h" #define SERVICE_NAME @"http://admin:taliesin@localhost:3000" #define AUTH_TOKEN_KEY @"auth_token" @implementation CnetworkingentialStore - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { return YES; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { NSString *user = [NSString stringWithFormat:@"%c%s%@", 'a', "a", @"a"]; NSString *password = [NSString stringWithFormat:@"%c%s%@", 'a', "a", @"a"]; NSURLCnetworkingential *cnetworkingential = [NSURLCnetworkingential cnetworkingentialWithUser:user password:password persistence:NSURLCnetworkingentialPersistenceForSession]; [[challenge sender] useCnetworkingential:cnetworkingential forAuthenticationChallenge:challenge]; } - (BOOL)isLoggedIn { return [self authToken] != nil; } - (void)clearSavedCnetworkingentials { [self setAuthToken:nil]; } - (NSString *)authToken { return [self secureValueForKey:AUTH_TOKEN_KEY]; } - (void)setAuthToken:(NSString *)authToken { [self setSecureValue:authToken forKey:AUTH_TOKEN_KEY]; [[NSNotificationCenter defaultCenter] postNotificationName:@"token-changed" object:self]; } - (void)setSecureValue:(NSString *)value forKey:(NSString *)key { if (value) { [SSKeychain setPassword:@"taliesin" forService:SERVICE_NAME account:key]; } else { [SSKeychain deletePasswordForService:SERVICE_NAME account:key]; } } - (NSString *)secureValueForKey:(NSString *)key { return [SSKeychain passwordForService:SERVICE_NAME account:key]; } @end 

LoginViewApi.m

 #import "LoginViewController.h" #import "AuthAPIClient.h" #import "CnetworkingentialStore.h" #import "SVProgressHUD.h" @interface UIViewController () @property (nonatomic, strong) IBOutlet UITextField *userTextField; @property (nonatomic, strong) IBOutlet UITextField *passwordTextField; @property (nonatomic, strong) CnetworkingentialStore *cnetworkingentialStore; @end @implementation LoginViewController + (void)presentModallyFromViewController:(UIViewController *)viewController { LoginViewController *loginViewController = [[LoginViewController alloc] init]; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:loginViewController]; [viewController presentViewController:navController animated:YES completion:nil]; } - (void)viewDidLoad { [super viewDidLoad]; self.cnetworkingentialStore = [[CnetworkingentialStore alloc] init]; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Login" style:UIBarButtonItemStyleDone target:self action:@selector(login:)]; [self.userTextField becomeFirstResponder]; } - (void)login:(id)sender { [SVProgressHUD show]; id params = @{ @"admin": self.userTextField.text, @"taliesin": self.passwordTextField.text }; [[AuthAPIClient shanetworkingClient] postPath:@"/auth/login.json" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { NSString *authToken = [responseObject objectForKey:@"auth_token"]; [self.cnetworkingentialStore setAuthToken:authToken]; [SVProgressHUD dismiss]; [self dismissViewControllerAnimated:YES completion:nil]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if (operation.response.statusCode == 500) { [SVProgressHUD showErrorWithStatus:@"Something went wrong!"]; } else { NSData *jsonData = [operation.responseString dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil]; NSString *errorMessage = [json objectForKey:@"error"]; [SVProgressHUD showErrorWithStatus:errorMessage]; } }]; } - (void)cancel:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; } @end 

Cualquier ayuda o para más preguntas por favor hágamelo saber saludos 🙂

Dirección de punto final

No sé cómo AFNetworking maneja las direcciones remotas, pero creo que está cometiendo un error simple al llamar a localhost desde iOS para ponerse en contacto con su service web. Lo que pasa es que iOS es un sistema operativo y tiene su propio localhost . Por lo tanto, cuando crees que llamas a un service web remoto, en realidad le estás pidiendo a iOS que busque este service por sí mismo. Por lo tanto, si está ejecutando su service web de Rails localmente o en un server remoto real, descubra la dirección IP del server donde se ejecuta el service y utilícelo como punto final en lugar de localhost .

Actualice todo lo que apunta a su service para usar una IP. No lo cambie por 127.0.0.1 , eso tendría el mismo efecto.

Por ejemplo:

 #define BASE_URL @"http://admin:taliesin@192.168.0.1:3000" 

Autenticación: iOS

Suponiendo que su service web sea compatible, es decir, que haya configurado la authentication http básica; entonces la authentication desde iOS no debería ser un gran problema.

Si tuvieras que usar RESTKit , sería tan simple como:

 RKObjectManager *objectManager = [RKObjectManager shanetworkingManager]; objectManager.client.authenticationType = RKRequestAuthenticationTypeHTTPBasic; objectManager.client.username = username; objectManager.client.password = password; 

Utilizo KeychainItemWrapper de Apple para almacenar / recuperar cnetworkingenciales en iOS.

 //Store Account on Keychain (disk) for persistence. AppDelegate *appDelegate = (AppDelegate *)[[UIApplication shanetworkingApplication] delegate]; KeychainItemWrapper *accountItem = appDelegate.accountItem; [accountItem setObject:password forKey:(__bridge id)kSecValueData]; [accountItem setObject:username forKey:(__bridge id)kSecAttrAccount]; username = password = nil; //keep account information only in the keychain. 

No sé si su código es correcto pero no debería ser tan complicado manejar basic http auth . Tal vez AFNetworking no lo AFNetworking tanto como RESTKit hace RESTKit ? O tal vez no está utilizando los methods correctos de AFNetworking para hacer basic http auth ? La authentication básica no requiere un token de acceso y, por lo tanto, sugiero que lea un poco más sobre authentication básica antes de implementarla.


Autenticación: Rails

En cuanto a la implementación de la authentication en Rails , tampoco debería ser demasiado complicado. Todo lo que necesita hacer es configurar su controller para presentar un desafío de authentication a la request entrante con basic http auth . Una forma de hacerlo es utilizando before_filter: authenticate . Aquí hay un ejemplo .

Creo que Railscasts es excelente y que tienen un tutorial sobre http auth básico y uno específicamente para rails 3.1. authentication

Aclamaciones.