iOS:如何在应用程序中存储用户名/密码?

时间:2011-08-07 10:10:13

标签: ios nsuserdefaults keychain

我的iOS应用程序中有一个登录屏幕。 用户名和密码将保存在NSUserDefaults中,并在您再次输入应用时再次加载到登录屏幕中(当然,NSUserDefaults是永久性的。)

现在,用户可以禁用用户名/密码保存功能。

那么NSUserDefaults将被清除。

但是在我的应用程序中,我需要这个用户名/密码来为用户提供数据库查询。 那么:存储除NSUserDefaults以外的数据的位置? (当用户退出应用程序或注销时,可以/应该删除此地点。)

15 个答案:

答案 0 :(得分:419)

您应该始终使用Keychain来存储用户名和密码,并且由于它是安全存储的,并且只能由您的应用访问,因此无需在应用退出时将其删除(如果您担心这一点)。

Apple提供存储,读取和删除钥匙串项目的sample code,以及如何使用该示例中的钥匙串包装类,这大大简化了使用Keychain。

包含Security.framework (在Xcode 3中右键单击frameworks文件夹并添加现有框架。在Xcode 4中选择您的项目,然后选择目标,转到Build Phases选项卡并单击Link Binary With Files下) 和KeychainItemWrapper .h& .m文件到您的项目中,#import您需要使用keychain的.h文件,然后创建该类的实例:

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

YourAppLogin 可以是您选择调用钥匙串项目的任何内容,如果需要,您可以拥有多个项目)

然后您可以使用以下方式设置用户名和密码:

[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];

答案 1 :(得分:97)

如果您需要ARC版本的包装,请点击链接https://gist.github.com/1170641 感谢

答案 2 :(得分:48)

通过Keychains非常简单的解决方案。

这是系统Keychain的简单包装器。只需将SSKeychain.hSSKeychain.mSSKeychainQuery.hSSKeychainQuery.m文件添加到项目中,然后将Security.framework添加到目标中。

保存密码:

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

要检索密码:

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

setPassword是您想要保存的值,forService是您想要保存的变量,而帐户是用户/对象的密码和任何其他信息。

答案 3 :(得分:27)

您只需使用NSURLCredential,只需两行代码就可以将密码链中的用户名和密码保存为

请参阅我的详细answer

答案 4 :(得分:13)

我决定使用Obj-C和ARC来回答如何在iOS 8中使用keychain。

1)我使用了GIST的keychainItemWrapper(ARCifief版本): 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;

享受!

答案 5 :(得分:11)

如果您在使用钥匙串包装器检索密码时遇到问题,请使用以下代码:

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

答案 6 :(得分:3)

结帐sample code 我首先尝试了示例代码中的apple的包装器,但这对我来说简单得多

答案 7 :(得分: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];

可能会有所帮助。

答案 8 :(得分:2)

我查看了使用KeychainItemWrapper(ARC版本),但我发现它的Objective C包装器并不像所希望的那样健康。

我使用了this solution 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"];

答案 9 :(得分:2)

There is a small bug in the above code (by the way Dave it was very helpful your post thank you)

In the part where we save the credentials it also needs the following code in order to work properly.

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

most probably is because the second time we try to (re-)sign in with the same credentials it finds them already assigned in the keychain items and the app crashes. with the above code it works like a charm.

答案 10 :(得分:2)

更新此问题:

对于那些使用Swift结帐的人来说,Mihai Costea支持访问组的拖放操作很快:

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

使用钥匙串之前: 在存储密码之前考虑两次。在许多情况下,存储身份验证令牌(例如持久性会话ID)和电子邮件或帐户名称可能就足够了。您可以轻松地使身份验证令牌无效以阻止未经授权的访问,要求用户再次登录受感染的设备但不需要重置密码并且必须在所有设备上再次登录(我们不仅仅使用Apple?)。

答案 11 :(得分:1)

但是,现在你可以使用NURLCredential而不是keychain包装器。它完成了我们需要做的事情。

答案 12 :(得分:1)

对于swift,你可以使用这个库:

https://github.com/jrendel/SwiftKeychainWrapper

它支持所有版本的swift。

答案 13 :(得分: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];

答案 14 :(得分:0)

如果您的应用有关联的域,最好使用 Managing Shared Credentials。它将为用户提供最佳体验,并由系统自行管理。

第 1 步:

使用 webcredentials 密钥设置您的 Associated Domains

第 2 步

标记您的文本字段

userIdTextField.textContentType = .username
passwordTextField.textContentType = .password

步骤 3

每当用户成功登录时,使用以下代码保存详细信息。 iOS 将显示一个确认操作表供用户保存密码。下次用户尝试登录时,键盘会建议您的应用程序的用户凭据。

SecAddSharedWebCredential("www.example.com" as CFString, "user_id" as CFString, "password" as CFString) { error in
    if error != nil {
      // Handle error
      return
    }

    // The credentials have been successfully saved.
}

现在你准备好了。 Read more...?!