iOS: как сохранить имя пользователя / пароль в приложении?


276

У меня есть экран входа в моем приложении iOS. Имя пользователя и пароль будут сохранены в NSUserDefaultsи снова загружены в экран входа в систему при повторном входе в приложение (конечно, NSUserDefaultsпостоянные).

Теперь у пользователя есть возможность отключить функцию сохранения имени пользователя / пароля.

Таким образом, NSUserDefaultsбудет очищен тогда.

Но в моем приложении мне нужно это имя пользователя / пароль для запросов к базе данных для пользователя. Итак: где хранить данные кроме NSUserDefaults? (Это место можно / нужно удалить, когда пользователь выйдет из приложения или выйдет из системы).


Пользователь может очистить его только путем сброса устройства или удаления приложения. Я что-то упускаю?

3
И, кстати, если данные должны быть удалены при выходе пользователя из приложения, почему бы просто не сохранить их в оперативной памяти?

10
Вы должны серьезно рассмотреть возможность использования Keychain для хранения имен пользователей и паролей вместо NSUserDefaults.
Филипп Раделик

1
Вы можете получить основную идею о реализации swift3 здесь
Anish Parajuli 웃

Пожалуйста, я должен всегда использовать kSecValueData и kSecValueData в качестве ключей? Или я могу использовать любую строку в качестве ключа?
Не А.С.

Ответы:


424

Вы всегда должны использовать связку ключей для хранения имен пользователей и паролей, и, поскольку они хранятся надежно и доступны только для вашего приложения, нет необходимости удалять их при выходе из приложения (если это было вашей проблемой).

Apple предоставляет пример кода, который хранит, считывает и удаляет элементы цепочки для ключей, и вот как использовать класс оболочки цепочки для ключей из этого примера, что значительно упрощает использование цепочки для ключей.

Включите Security.framework (в Xcode 3 щелкните правой кнопкой мыши папку фреймворков и добавьте существующий каркас. В Xcode 4 выберите свой проект, затем выберите цель, перейдите на вкладку «Фазы сборки» и нажмите «+» в разделе «Связать двоичные файлы с файлами») и KeychainItemWrapper .h &. m файлов в ваш проект, # импортируйте файл .h везде, где вам нужно использовать цепочку для ключей, а затем создайте экземпляр этого класса:

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

( YourAppLogin может быть любым, что вы выбрали для вызова элемента Keychain, и вы можете иметь несколько элементов при необходимости)

Затем вы можете установить имя пользователя и пароль, используя:

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

Получить их с помощью:

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

Или удалите их, используя:

[keychainItem resetKeychainItem];

5
Я обновил свой ответ с кодом и описанием. Это не так сложно, как вы думали.
Филипп Раделик

44
ATTANTION! Пожалуйста, добавьте к своему ответу, что только «copy KeychainItemWrapper» недостаточно! У меня была проблема, что я не могу построить это потом! Вы должны добавить security.framework в свой проект, чтобы KeychainItemWrapper работал! (Как: Выбрать проект -> Выбрать цель -> Выбрать вкладку «Фазы сборки» -> Выбрать «Связать двоичные файлы с библиотеками» -> «+» -> добавить Security.Framework)
Kovu

63
При использовании ARC компилятор будет кричать на вас за использование констант kSecValueDataи kSecAttrAccountв коде Objective-C, поэтому обязательно приведите их, используя (__bridge id), например, [keychainItem setObject:obj forKey:(__bridge id)kSecValueData];
Joe Hankin

7
Кажется, что KeychainItemWrapper.m имеет утечку памяти в строке 196. Изменение строки на «self.keychainItemData = [[[NSMutableDictionary alloc] init] autorelease];» исправляет это.
Олоф

12
При использовании ARC обновленный код был дан здесь Яблоком. Посмотрите на Перечисление 2-1. Хотя подход такой же.
Рик


48

Очень простое решение с помощью брелков .

Это простая оболочка для системы Keychain. Просто добавьте SSKeychain.h, SSKeychain.m, SSKeychainQuery.hи SSKeychainQuery.mфайлы в свой проект и добавить Security.framework к вашей цели.

Чтобы сохранить пароль:

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

Чтобы восстановить пароль:

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

Где setPasswordнаходится то значение, которое вы хотите сохранить, и forServiceкакая переменная, под которой вы хотите сохранить, и учетная запись для какого пользователя / объекта пароль и любая другая информация.


Знаете ли вы, как использовать sskeychain для синхронизации приложений с одинаковыми именем пользователя и паролем?
Джо Шамурак

4
как вы храните имя пользователя в дополнение к паролю? Как удалить всю учетную запись из SSKeychain? Оба не упоминаются документы
user798719

2
Чтобы получить имя пользователя сделать NSString *username = [[SSKeychain accountsForService:@"AnyService"][0] valueForKey:@"acct"]. Это должно работать нормально, если вы используете только одну учетную запись. Как всегда, не забудьте проверить длину массива, прежде чем пытаться получить доступ к индексу 0.
Джаред Прайс

27

Вы можете просто использовать NSURLCredential, он сохранит имя пользователя и пароль в цепочке для ключей всего в двух строках кода .

Смотрите мой подробный ответ .


13

Я решил ответить, как использовать связку ключей в iOS 8, используя Obj-C и ARC.

1) Я использовал keychainItemWrapper (версия ARCifief) из GIST: https://gist.github.com/dhoerl/1170641/download - добавьте (+ скопируйте) KeychainItemWrapper.h и .m в ваш проект

2) Добавьте каркас безопасности в ваш проект (проверьте в проекте> Фазы сборки> Связать бинарный файл с библиотеками)

3) Добавьте библиотеку безопасности (#import) и KeychainItemWrapper (#import "KeychainItemWrapper.h") в файлы .h и .m, где вы хотите использовать цепочку ключей.

4) Чтобы сохранить данные в связку ключей:

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) Считать данные (возможно, экран входа в систему при загрузке> 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;

Наслаждайтесь!


11

Если у вас возникли проблемы с получением пароля с помощью оболочки ключей, используйте этот код:

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

3

зацените этот пример кода, я сначала попробовал оболочку яблока из примера кода, но для меня это намного проще


2

Попробуй это:

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

может это поможет.


2

Я смотрел на использование KeychainItemWrapper (версия ARC), но я не нашел его обертку Objective C настолько полезной, насколько хотелось.

Я использовал это решение от Kishikawa Katsumi , что означало, что я написал меньше кода и мне не пришлось использовать приведения для хранения значений NSString.

Два примера хранения:

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

Два примера получения

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

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

2

В приведенном выше коде есть небольшая ошибка (кстати, Дэйв, это было очень полезно, ваш пост, спасибо)

В той части, где мы сохраняем учетные данные, для правильной работы также необходим следующий код.

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

наиболее вероятно, потому что во второй раз, когда мы пытаемся (повторно) войти в систему с теми же учетными данными, он находит их уже назначенными в элементах цепочки для ключей, и приложение вылетает. с приведенным выше кодом это работает как шарм.


2

Чтобы обновить этот вопрос:

Для тех, кто использует Swift checkout, эта реализация Swift перетаскивания от Mihai Costea поддерживает группы доступа:

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

Перед использованием цепочки для ключей: дважды подумайте, прежде чем хранить пароли. Во многих случаях может быть достаточно сохранения токена аутентификации (такого как идентификатор сеанса постоянства) и электронной почты или имени учетной записи. Вы можете легко аннулировать токены аутентификации, чтобы заблокировать несанкционированный доступ, требуя от пользователя повторной регистрации на скомпрометированном устройстве, но не требуя сброса пароля и повторной регистрации на всех устройствах (мы не только используем Apple, не так ли?).


1

Но теперь вы можете использовать NURLCredential вместо оболочки для ключей. Он делает то, что нам нужно сделать.



0

Следующее должно работать просто отлично:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[keychainItem setObject:@"password you are saving" forKey:kSecValueData]; 
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.