iOS: jak przechowywać nazwę użytkownika / hasło w aplikacji?

276

Mam ekran logowania w mojej aplikacji na iOS. Nazwa użytkownika i hasło zostaną zapisane NSUserDefaultsi ponownie załadowane na ekran logowania po ponownym wejściu do aplikacji (oczywiście NSUserDefaultssą stałe).

Teraz użytkownik ma możliwość wyłączenia funkcji zapisywania nazwy użytkownika / hasła.

Więc to NSUserDefaultszostanie wyczyszczone.

Ale w mojej aplikacji potrzebuję tej nazwy użytkownika / hasła do zapytań do bazy danych dla użytkownika. Więc: Gdzie przechowywać dane oprócz NSUserDefaults? (To miejsce może / powinno zostać usunięte, gdy użytkownik zamknie aplikację lub wyloguje się).

Kovu
źródło
Użytkownik może to wyczyścić tylko poprzez zresetowanie urządzenia lub usunięcie aplikacji. Czy coś brakuje?
3
Nawiasem mówiąc, jeśli dane powinny zostać usunięte, gdy użytkownik zamknie aplikację, dlaczego nie przechowywać ich w pamięci RAM?
10
Powinieneś poważnie rozważyć użycie pęku kluczy do przechowywania nazw użytkowników i haseł zamiast NSUserDefaults.
Filip Radelic,
1
Można uzyskać podstawowe pomysł na swift3 realizacji od tutaj
Anish Parajuli 웃
Czy zawsze powinienem używać kSecValueData i kSecValueData jako kluczy? Czy mogę użyć dowolnego ciągu jako klucza?
Ne AS

Odpowiedzi:

424

Zawsze należy używać pęku kluczy do przechowywania nazw użytkowników i haseł, a ponieważ jest on przechowywany bezpiecznie i dostępny tylko dla Twojej aplikacji, nie ma potrzeby usuwania go po zamknięciu aplikacji (jeśli to było twoje zmartwienie).

Apple udostępnia przykładowy kod, który przechowuje, odczytuje i usuwa elementy pęku kluczy, a oto sposób użycia klasy opakowania pęku kluczy z tej próbki, co znacznie upraszcza korzystanie z pęku kluczy.

Dołącz Security.framework (w Xcode 3 kliknij prawym przyciskiem myszy folder frameworks i dodaj istniejącą ramę. W Xcode 4 wybierz projekt, następnie wybierz cel, przejdź do zakładki Fazy kompilacji i kliknij + w obszarze Połącz pliki binarne z plikami) i KeychainItemWrapper .h &. m plików do swojego projektu, #importuj plik .h wszędzie tam, gdzie potrzebujesz użyć pęku kluczy, a następnie utwórz instancję tej klasy:

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

( YourAppLogin może być dowolną nazwą twojego elementu pęku kluczy i możesz mieć wiele elementów w razie potrzeby)

Następnie możesz ustawić nazwę użytkownika i hasło, używając:

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

Zdobądź je, używając:

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

Lub usuń je za pomocą:

[keychainItem resetKeychainItem];
Filip Radelic
źródło
5
Zaktualizowałem swoją odpowiedź kodem i opisem. To nie jest tak trudne, jak myślałeś.
Filip Radelic,
44
UWAGA! Dodaj do swojej odpowiedzi, że tylko „skopiuj KeychainItemWrapper” nie wystarczy! Miałem problem, że nie mogę go później zbudować! Musisz dodać security.framework do swojego projektu, że KeychainItemWrapper będzie działał! (HowTo: wybierz Project -> Select Target -> wybierz zakładkę "Etapy Build" -> Wybierz "Link binarnych z bibliotekami" -> "+" -> dodaj Security.Framework)
Kovu
63
Podczas korzystania z ARC kompilator będzie krzyczał na ciebie za użycie stałych kSecValueDataoraz kSecAttrAccountw kodzie Objective-C, więc pamiętaj, aby rzucić je za pomocą (__bridge id), np. [keychainItem setObject:obj forKey:(__bridge id)kSecValueData];
Joe Hankin
7
Wydaje się, że KeychainItemWrapper.m ma przeciek pamięci w linii 196. Zmiana linii na „self.keychainItemData = [[[NSMutableDictionary] init] autorelease];” naprawia to.
Olof,
12
Podczas korzystania z ARC Apple podał tutaj zaktualizowany kod . Spójrz na Listing 2-1. Chociaż podejście jest takie samo.
Rick
97

Jeśli potrzebujesz wersji opakowania ARC tutaj jest link https://gist.github.com/1170641 Dzięki

Manuel Pintaldi
źródło
Dzięki, otrzymuję KeychainItemWrapper .h & .m z tego adresu URL.
Parthpatel1105
48

Bardzo łatwe rozwiązanie za pomocą pęku kluczy .

Jest to proste opakowanie systemu pęku kluczy. Wystarczy dodać SSKeychain.h, SSKeychain.m, SSKeychainQuery.hi SSKeychainQuery.mpliki do projektu i dodać Security.framework do celu.

Aby zapisać hasło:

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

Aby odzyskać hasło:

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

Gdzie setPasswordjest wartość, którą chcesz zapisać, i forServicejaką zmienną chcesz zapisać, a konto jest dla jakiego użytkownika / obiektu jest hasło i wszelkie inne informacje.

sust86
źródło
Czy wiesz, jak używać sskeychain do synchronizowania aplikacji z tą samą nazwą użytkownika i hasłem?
Joe Shamuraq
4
jak przechowujesz nazwę użytkownika oprócz hasła? Jak usunąć całe konto z SSKeychain? Oba nie są wymienione w dokumentacji
user798719
2
Aby uzyskać nazwę użytkownika, zrób to NSString *username = [[SSKeychain accountsForService:@"AnyService"][0] valueForKey:@"acct"]. Powinno to działać poprawnie, jeśli używasz tylko jednego konta. Jak zawsze sprawdź długość tablicy przed próbą uzyskania dostępu do indeksu 0.
Jared Price
27

Możesz po prostu użyć NSURLCredential, zapisze zarówno nazwę użytkownika, jak i hasło w pęku kluczy w zaledwie dwóch liniach kodu .

Zobacz moją szczegółową odpowiedź .

Phil
źródło
13

Postanowiłem odpowiedzieć, jak używać pęku kluczy w iOS 8, używając Obj-C i ARC.

1) Użyłem keychainItemWrapper (wersja ARCifief) z GIST: https://gist.github.com/dhoerl/1170641/download - Dodaj (+ skopiuj) KeychainItemWrapper.h i .m do twojego projektu

2) Dodaj strukturę zabezpieczeń do swojego projektu (sprawdź w projekcie> Fazy budowania> Połącz pliki binarne z bibliotekami)

3) Dodaj bibliotekę zabezpieczeń (#import) i KeychainItemWrapper (#import „KeychainItemWrapper.h”) do pliku .h i .m, w którym chcesz użyć pęku kluczy.

4) Aby zapisać dane w pęku kluczy:

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) Odczytaj dane (prawdopodobnie ekran logowania podczas ładowania> 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;

Cieszyć się!

D_RBD
źródło
11

Jeśli masz problem z odzyskaniem hasła za pomocą opakowania pęku kluczy, użyj tego kodu:

NSData *pass =[keychain objectForKey:(__bridge id)(kSecValueData)];
NSString *passworddecoded = [[NSString alloc] initWithData:pass
                                           encoding:NSUTF8StringEncoding];
Robby891
źródło
3

sprawdź ten przykładowy kod próbowałem najpierw otoki jabłka z przykładowego kodu, ale jest to dla mnie o wiele prostsze

BSevo
źródło
2

Spróbuj tego:

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

niech to pomoże.

Vara
źródło
2

Spojrzałem na użycie KeychainItemWrapper (wersja ARC), ale nie znalazłem tego opakowania Objective C tak zdrowe, jak to pożądane.

Użyłem tego rozwiązania przez Kishikawa Katsumi , co oznaczało pisałem mniej kodu i nie trzeba używać odlewane do przechowywania wartości NSString.

Dwa przykłady przechowywania:

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

Dwa przykłady pobierania

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

NSString *username = [store stringForKey:@"username"];
NSString *password = [store stringForKey:@"password"];
Carl
źródło
2

W powyższym kodzie jest mały błąd (przy okazji, Dave bardzo pomógł Twojemu postowi, dziękuję)

W części, w której zapisujemy dane uwierzytelniające, potrzebny jest również następujący kod, aby działać poprawnie.

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

najprawdopodobniej dlatego, że po raz drugi próbujemy (ponownie) zalogować się przy użyciu tych samych poświadczeń, aby znaleźć je już przypisane w elementach pęku kluczy i aplikacja ulega awarii. z powyższym kodem działa jak urok.

Rzeczywistość
źródło
2

Aby zaktualizować to pytanie:

Dla tych, którzy korzystają z usługi Swift, ta implementacja szybkiego przeciągania i upuszczania przez Mihai Costea obsługuje grupy dostępu:

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

Przed użyciem pęku kluczy: rozważ dwa razy, zanim zapiszesz hasło. W wielu przypadkach przechowywanie tokena uwierzytelniającego (takiego jak identyfikator sesji trwałej) i adres e-mail lub nazwa konta mogą być wystarczające. Możesz łatwo unieważnić tokeny uwierzytelniające, aby zablokować nieautoryzowany dostęp, wymagając od użytkownika ponownego zalogowania się na zaatakowanym urządzeniu, ale nie wymagając resetowania hasła i konieczności ponownego zalogowania się na wszystkich urządzeniach (my nie tylko używamy Apple'a?).

pizzamonster
źródło
1

Ale teraz możesz wybrać NURLCredential zamiast opakowania pęku kluczy. Robi to, co musimy zrobić.

Sumitiscreative
źródło
0

Następujące elementy powinny działać dobrze:

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
źródło