iOS: Comment stocker le nom d'utilisateur / mot de passe dans une application?

276

J'ai un écran de connexion dans mon application iOS. Le nom d'utilisateur et le mot de passe seront enregistrés dans le NSUserDefaultset seront à nouveau chargés dans l'écran de connexion lorsque vous entrez à nouveau dans l'application (bien sûr, ils NSUserDefaultssont permanents).

Maintenant, l'utilisateur a la possibilité de désactiver la fonction d'enregistrement du nom d'utilisateur / mot de passe.

Le NSUserDefaultssera alors effacé.

Mais dans mon application, j'ai besoin de ce nom d'utilisateur / mot de passe pour les requêtes de base de données pour l'utilisateur. Donc: où stocker les données sauf NSUserDefaults? (Cet endroit peut / devrait être supprimé lorsque l'utilisateur quitte l'application ou se déconnecte).

Kovu
la source
L'utilisateur ne peut l'effacer qu'en réinitialisant l'appareil ou en supprimant l'application. Suis-je en train de manquer quelque chose?
3
Et au fait, si les données doivent être supprimées lorsque l'utilisateur quitte l'application, pourquoi ne pas simplement les conserver dans la RAM?
10
Vous devriez sérieusement envisager d'utiliser le trousseau pour stocker les noms d'utilisateur et les mots de passe au lieu de NSUserDefaults.
Filip Radelic
1
Vous pouvez avoir une idée de base sur la mise en œuvre de swift3 à partir d' ici
Anish Parajuli 웃
Dois-je toujours utiliser kSecValueData et kSecValueData comme clés? Ou puis-je utiliser une chaîne comme clé?
Ne AS

Réponses:

424

Vous devez toujours utiliser le trousseau pour stocker les noms d'utilisateur et les mots de passe, et comme il est stocké en toute sécurité et uniquement accessible à votre application, il n'est pas nécessaire de le supprimer lorsque l'application se ferme (si cela vous préoccupait).

Apple fournit un exemple de code qui stocke, lit et supprime les éléments de trousseau et voici comment utiliser la classe wrapper de trousseau de cet exemple, ce qui simplifie considérablement l'utilisation du trousseau.

Incluez Security.framework (dans Xcode 3, faites un clic droit sur le dossier des frameworks et ajoutez un framework existant. Dans Xcode 4, sélectionnez votre projet, puis sélectionnez la cible, allez dans l'onglet Build Phases et cliquez sur + sous Link Binary With Files) et KeychainItemWrapper .h &. m fichiers dans votre projet, # importez le fichier .h partout où vous devez utiliser le trousseau, puis créez une instance de cette classe:

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

( YourAppLogin peut être tout ce que vous avez choisi d'appeler votre élément de trousseau et vous pouvez avoir plusieurs éléments si nécessaire)

Ensuite, vous pouvez définir le nom d'utilisateur et le mot de passe en utilisant:

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

Obtenez-les en utilisant:

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

Ou supprimez-les en utilisant:

[keychainItem resetKeychainItem];
Filip Radelic
la source
5
J'ai mis à jour ma réponse avec le code et la description. Ce n'est pas aussi difficile que vous le pensiez.
Filip Radelic
44
ATTANTION! Veuillez ajouter à votre réponse, que seul "copier KeychainItemWrapper" ne suffit pas! J'ai eu le problème, je ne peux pas le construire après! Vous devez ajouter le security.framework à votre projet pour que KeychainItemWrapper fonctionne! (HowTo: Sélectionnez Project -> Select Target -> Select Tab "Build Phases" -> Select "Link Binary With Libaries" -> "+" -> add Security.Framework)
Kovu
63
Lorsque vous utilisez ARC, le compilateur vous hurlera d'utiliser les constantes kSecValueDataet le kSecAttrAccountcode Objective-C, alors assurez-vous de les transtyper en utilisant (__bridge id), par exemple, [keychainItem setObject:obj forKey:(__bridge id)kSecValueData];
Joe Hankin
7
KeychainItemWrapper.m semble avoir une fuite de mémoire à la ligne 196. Changer la ligne en "self.keychainItemData = [[[NSMutableDictionary alloc] init] autorelease];" le corrige.
Olof
12
Lors de l'utilisation d'ARC, le code mis à jour a été fourni ici par Apple. Regardez l'extrait 2-1. Bien que l'approche soit la même.
Rick
97

Si vous avez besoin d'une version ARC du wrapper voici le lien https://gist.github.com/1170641 Merci à

Manuel Pintaldi
la source
Merci, je reçois KeychainItemWrapper .h & .m de cette URL.
Parthpatel1105
48

Une solution très simple via des porte-clés .

C'est un simple wrapper pour le trousseau du système. Il suffit d' ajouter les SSKeychain.h, SSKeychain.m, SSKeychainQuery.het les SSKeychainQuery.mfichiers à votre projet et ajoutez le Security.framework à votre cible.

Pour enregistrer un mot de passe:

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

Pour récupérer un mot de passe:

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

setPasswordest la valeur que vous souhaitez enregistrer et forServicequelle est la variable sous laquelle vous voulez qu'il soit enregistré et le compte est pour quel utilisateur / objet le mot de passe et toute autre information sont destinés.

sust86
la source
Savez-vous comment utiliser sskeychain pour synchroniser des applications avec le même nom d'utilisateur et le même mot de passe?
Joe Shamuraq
4
comment conservez-vous également un nom d'utilisateur en plus d'un mot de passe? Comment supprimer un compte entier du SSKeychain? Les deux ne sont pas mentionnés dans les documents
user798719
2
Pour obtenir le nom d'utilisateur, faites NSString *username = [[SSKeychain accountsForService:@"AnyService"][0] valueForKey:@"acct"]. Cela devrait fonctionner correctement si vous n'utilisez qu'un seul compte. Comme toujours, assurez-vous de vérifier la longueur du tableau avant d'essayer d'accéder à l'index 0.
Jared Price
27

Vous pouvez simplement l'utiliser NSURLCredential, il enregistrera le nom d'utilisateur et le mot de passe dans le trousseau en seulement deux lignes de code .

Voir ma réponse détaillée .

Phil
la source
13

J'ai décidé de répondre à la façon d'utiliser le trousseau dans iOS 8 en utilisant Obj-C et ARC.

1) J'ai utilisé le keychainItemWrapper (version ARCifief) de GIST: https://gist.github.com/dhoerl/1170641/download - Ajoutez (+ copie) le KeychainItemWrapper.h et .m à votre projet

2) Ajoutez le cadre de sécurité à votre projet (archivez le projet> Phases de construction> Lier le binaire aux bibliothèques)

3) Ajoutez la bibliothèque de sécurité (#import) et KeychainItemWrapper (#import "KeychainItemWrapper.h") au fichier .h et .m dans lequel vous souhaitez utiliser le trousseau.

4) Pour enregistrer les données dans le trousseau:

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) Lire les données (probablement l'écran de connexion lors du chargement> 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;

Prendre plaisir!

D_RBD
la source
11

Si vous rencontrez un problème lors de la récupération du mot de passe à l'aide de l'encapsuleur de trousseau, utilisez ce code:

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

vérifier cet exemple de code, j'ai d'abord essayé le wrapper de la pomme à partir de l'exemple de code, mais c'est beaucoup plus simple pour moi

BSevo
la source
2

essaye celui-là:

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

puisse-t-il aider.

Vara
la source
2

J'ai envisagé d'utiliser KeychainItemWrapper (la version ARC) mais je n'ai pas trouvé son wrapper Objective C aussi sain que souhaité.

J'ai utilisé cette solution de Kishikawa Katsumi , ce qui signifie que j'ai écrit moins de code et que je n'ai pas eu besoin de transtypages pour stocker les valeurs NSString.

Deux exemples de stockage:

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

Deux exemples de récupération

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

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

Il y a un petit bug dans le code ci-dessus (au fait, Dave, cela a été très utile, merci)

Dans la partie où nous enregistrons les informations d'identification, il a également besoin du code suivant pour fonctionner correctement.

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

très probablement parce que la deuxième fois que nous essayons de (re) connecter avec les mêmes informations d'identification, il les trouve déjà attribuées dans les éléments du trousseau et l'application se bloque. avec le code ci-dessus, cela fonctionne comme un charme.

Réalité
la source
2

Pour mettre à jour cette question:

Pour ceux qui utilisent Swift Checkout, cette implémentation rapide de glisser-déposer par Mihai Costea prend en charge les groupes d'accès:

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

Avant d'utiliser le trousseau: pensez à deux fois avant de stocker les mots de passe. Dans de nombreux cas, le stockage d'un jeton d'authentification (tel qu'un identifiant de session de persistance) et le nom de messagerie ou de compte peuvent être suffisants. Vous pouvez facilement invalider les jetons d'authentification pour bloquer tout accès non autorisé, obligeant l'utilisateur à se reconnecter sur l'appareil compromis mais pas à réinitialiser le mot de passe et à se reconnecter sur tous les appareils (nous n'utilisons pas seulement Apple, n'est-ce pas?).

pizzamonster
la source
1

Mais, maintenant, vous pouvez opter pour NURLCredential au lieu de l'encapsuleur de trousseau. Il fait ce que nous devons faire.

Sumitiscreative
la source
0

Les éléments suivants devraient très bien fonctionner:

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
la source