iOS: ¿Cómo almacenar nombre de usuario / contraseña dentro de una aplicación?

276

Tengo una pantalla de inicio de sesión en mi aplicación iOS. El nombre de usuario y la contraseña se guardarán en el NSUserDefaultsy se cargarán nuevamente en la pantalla de inicio de sesión cuando vuelva a ingresar a la aplicación (por supuesto, NSUserDefaultsson permanentes).

Ahora, el usuario tiene la posibilidad de desactivar la función de guardado de nombre de usuario / contraseña.

Entonces el NSUserDefaultsserá borrado entonces.

Pero en mi aplicación necesito este nombre de usuario / contraseña para consultas de la base de datos para el usuario. Entonces: ¿Dónde almacenar los datos excepto NSUserDefaults? (Este lugar puede / debe eliminarse cuando el usuario cierra la aplicación o cierra sesión).

Kovu
fuente
El usuario solo puede borrarlo reiniciando el dispositivo o eliminando la aplicación. ¿Me estoy perdiendo de algo?
3
Y, por cierto, si los datos deben eliminarse cuando el usuario cierra la aplicación, ¿por qué no mantenerlos en la RAM?
10
Debería considerar seriamente usar Keychain para almacenar nombres de usuario y contraseñas en lugar de NSUserDefaults.
Filip Radelic
1
Puede obtener una idea básica sobre la implementación de swift3 desde aquí
Anish Parajuli 웃
¿Debería usar siempre kSecValueData y kSecValueData como claves? ¿O puedo usar cualquier cadena como clave?
Ne AS

Respuestas:

424

Siempre debe usar Keychain para almacenar nombres de usuario y contraseñas, y dado que se almacena de forma segura y solo es accesible para su aplicación, no hay necesidad de eliminarlo cuando la aplicación se cierra (si esa era su preocupación).

Apple proporciona un código de muestra que almacena, lee y elimina elementos de llavero y aquí se explica cómo usar la clase de contenedor de llavero de esa muestra, lo que simplifica enormemente el uso de Keychain.

Incluya Security.framework (en Xcode 3, haga clic con el botón derecho en la carpeta de frameworks y agregue el framework existente. En Xcode 4 seleccione su proyecto, luego seleccione el objetivo, vaya a la pestaña Build Fases y haga clic en + debajo de Link Binary With Files) y KeychainItemWrapper .h &. m archivos en su proyecto, # importe el archivo .h donde necesite usar llavero y luego cree una instancia de esta clase:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];

( YourAppLogin puede ser cualquier cosa que elija para llamar a su elemento de Llavero y puede tener varios elementos si es necesario)

Luego puede configurar el nombre de usuario y la contraseña usando:

[keychainItem setObject:@"password you are saving" forKey:kSecValueData];
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];

Consíguelos usando:

NSString *password = [keychainItem objectForKey:kSecValueData];
NSString *username = [keychainItem objectForKey:kSecAttrAccount];

O elimínelos usando:

[keychainItem resetKeychainItem];
Filip Radelic
fuente
55
He actualizado mi respuesta con el código y la descripción. No es tan difícil como pensabas.
Filip Radelic
44
ATENCION! Agregue a su respuesta que solo "copiar KeychainItemWrapper" no es suficiente. ¡Tuve el problema, que no puedo construirlo después! ¡Debe agregar security.framework a su proyecto para que funcione KeychainItemWrapper! (Cómo: Seleccionar proyecto -> Seleccionar destino -> Seleccionar pestaña "Fases de construcción" -> Seleccionar "Vincular binario con bibliotecas" -> "+" -> agregar seguridad
Marco
63
Cuando use ARC, el compilador le gritará por usar las constantes kSecValueDatay kSecAttrAccounten el código Objective-C, así que asegúrese de enviarlas usando (__bridge id), por ejemplo, [keychainItem setObject:obj forKey:(__bridge id)kSecValueData];
Joe Hankin el
77
KeychainItemWrapper.m parece tener una pérdida de memoria en la línea 196. Cambiar la línea a "self.keychainItemData = [[[NSMutableDictionary alloc] init] autorelease];" lo arregla
Olof
12
Al usar ARC, Apple ha proporcionado el código actualizado aquí . Mire el Listado 2-1. Aunque el enfoque es el mismo.
Rick
97

Si necesita una versión ARC del contenedor aquí está el enlace https://gist.github.com/1170641 Gracias a

Manuel Pintaldi
fuente
Gracias, obtengo KeychainItemWrapper .h & .m de esa URL.
Parthpatel1105
48

Una solución muy fácil a través de llaveros .

Es un contenedor simple para el sistema Keychain. Sólo tiene que añadir los SSKeychain.h, SSKeychain.m, SSKeychainQuery.hy SSKeychainQuery.mlos archivos a su proyecto y añadir el Security.framework a su objetivo.

Para guardar una contraseña:

[SSKeychain setPassword:@"AnyPassword" forService:@"AnyService" account:@"AnyUser"]

Para recuperar una contraseña:

NSString *password = [SSKeychain passwordForService:@"AnyService" account:@"AnyUser"];

¿Dónde setPasswordestá el valor que desea guardar y en forServicequé variable desea guardarlo? La cuenta es para qué usuario / objeto se utiliza la contraseña y cualquier otra información.

sust86
fuente
¿Sabes cómo usar sskeychain para sincronizar aplicaciones con el mismo nombre de usuario y contraseña?
Joe Shamuraq
44
¿Cómo se almacena un nombre de usuario además de una contraseña? ¿Cómo eliminas una cuenta completa de SSKeychain? Ambos no se mencionan los documentos
user798719
2
Para obtener el nombre de usuario hacer NSString *username = [[SSKeychain accountsForService:@"AnyService"][0] valueForKey:@"acct"]. Esto debería funcionar bien si solo usa una cuenta. Como siempre, asegúrese de verificar la longitud de la matriz antes de intentar acceder al índice 0.
Jared Price
27

Simplemente puede usar NSURLCredential, guardará tanto el nombre de usuario como la contraseña en el llavero en solo dos líneas de código .

Ver mi respuesta detallada .

Phil
fuente
13

Decidí responder cómo usar el llavero en iOS 8 usando Obj-C y ARC.

1) Utilicé keychainItemWrapper (versión ARCifief) de GIST: https://gist.github.com/dhoerl/1170641/download - Agregue (+ copie) KeychainItemWrapper.h y .m a su proyecto

2) Agregue el marco de seguridad a su proyecto (verifique proyecto> Fases de compilación> Enlace binario con bibliotecas)

3) Agregue la biblioteca de seguridad (#import) y KeychainItemWrapper (#import "KeychainItemWrapper.h") al archivo .h y .m donde desea usar keychain.

4) Para guardar datos en el llavero:

NSString *emailAddress = self.txtEmail.text;
NSString *password = self.txtPasword.text;
//because keychain saves password as NSData object
NSData *pwdData = [password dataUsingEncoding:NSUTF8StringEncoding];

//Save item
self.keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[self.keychainItem setObject:emailAddress forKey:(__bridge id)(kSecAttrAccount)];
[self.keychainItem setObject:pwdData forKey:(__bridge id)(kSecValueData)];

5) Leer datos (probablemente pantalla de inicio de sesión al cargar> viewDidLoad):

self.keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];

self.txtEmail.text = [self.keychainItem objectForKey:(__bridge id)(kSecAttrAccount)];

//because label uses NSString and password is NSData object, conversion necessary
NSData *pwdData = [self.keychainItem objectForKey:(__bridge id)(kSecValueData)];
NSString *password = [[NSString alloc] initWithData:pwdData encoding:NSUTF8StringEncoding];
self.txtPassword.text = password;

¡Disfrutar!

D_RBD
fuente
11

Si tiene problemas para recuperar la contraseña con el contenedor de llavero, use este código:

NSData *pass =[keychain objectForKey:(__bridge id)(kSecValueData)];
NSString *passworddecoded = [[NSString alloc] initWithData:pass
                                           encoding:NSUTF8StringEncoding];
Robby891
fuente
3

compruebe este código de muestra que probé primero el contenedor de la manzana del código de muestra, pero esto es mucho más simple para mí

BSevo
fuente
2

prueba este:

 KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[keychainItem setObject:@"password you are saving" forKey:kSecValueData]; 
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];

Que te ayude.

Vara
fuente
2

Observé el uso de KeychainItemWrapper (la versión ARC) pero no encontré su envoltorio de Objective C tan saludable como lo deseé.

Utilicé esta solución de Kishikawa Katsumi , lo que significaba que escribí menos código y no tuve que usar moldes para almacenar valores NSString.

Dos ejemplos de almacenamiento:

[UICKeyChainStore setString:@"kishikawakatsumi" forKey:@"username"];
[UICKeyChainStore setString:@"P455_w0rd$1$G$Z$" forKey:@"password"];

Dos ejemplos de recuperación

UICKeyChainStore *store = [UICKeyChainStore keyChainStore];
    // or
UICKeyChainStore *store = [UICKeyChainStore keyChainStoreWithService:@"YOUR_SERVICE"];

NSString *username = [store stringForKey:@"username"];
NSString *password = [store stringForKey:@"password"];
Carl
fuente
2

Hay un pequeño error en el código anterior (por cierto Dave, fue muy útil tu publicación, gracias)

En la parte donde guardamos las credenciales, también necesita el siguiente código para que funcione correctamente.

[self.keychainItem setObject:@"myCredentials" forKey:(__bridge id)(kSecAttrService)];

Lo más probable es que la segunda vez que intentamos (re) iniciar sesión con las mismas credenciales las encuentra ya asignadas en los elementos del llavero y la aplicación falla. con el código anterior funciona de maravilla.

Realidad
fuente
2

Para actualizar esta pregunta:

Para aquellos que usan el pago rápido, esta implementación rápida de arrastrar y soltar por Mihai Costea apoyando grupos de acceso:

https://github.com/macostea/KeychainItemWrapper.swift/blob/master/KeychainItemWrapper.swift

Antes de usar el llavero: considere dos veces antes de almacenar las contraseñas. En muchos casos, almacenar un token de autenticación (como una identificación de sesión de persistencia) y el correo electrónico o el nombre de la cuenta pueden ser suficientes. Puede invalidar fácilmente los tokens de autenticación para bloquear el acceso no autorizado, lo que requiere que el usuario inicie sesión nuevamente en el dispositivo comprometido, pero no requiere restablecer la contraseña y tener que iniciar sesión nuevamente en todos los dispositivos (no solo estamos usando Apple, ¿verdad?).

pizzamonster
fuente
1

Pero, ahora puede optar por NURLCredential en lugar de envoltorio de llavero. Hace lo que necesitamos hacer.

Sumitiscreativa
fuente
0

Lo siguiente debería funcionar bien:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[keychainItem setObject:@"password you are saving" forKey:kSecValueData]; 
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];
pkamb
fuente