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


276

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

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

因此,NSUserDefaults将清除。

但是在我的应用程序中,我需要此用户名/密码来进行用户的数据库查询。因此:除了在哪里存储数据NSUserDefaults?(当用户退出应用或退出时,可以/应该删除此地点)。


用户只能通过重置设备或删除应用程序来清除它。我想念什么吗?

3
顺便说一句,如果用户退出应用程序时应该删除数据,为什么不只将其保存在RAM中呢?

10
您应该认真考虑使用钥匙串而不是NSUserDefaults来存储用户名和密码。
Filip Radelic 2011年

1
您可以从此处
Anish Parajuli웃

请问我应该始终使用kSecValueData和kSecValueData作为键吗?还是可以将任何字符串用作键?
Ne AS

Answers:


424

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

Apple提供了存储,读取和删除钥匙串项目的示例代码,这是如何使用该示例中的钥匙串包装器类的方法,从而大大简化了钥匙串的使用。

包括Security.framework (在Xcode 3中,右键单击frameworks文件夹并添加现有框架。在Xcode 4中选择您的项目,然后选择目标,转到Build Phases选项卡,然后单击Link Binary With Files下的+)和KeychainItemWrapper .h&。 m个文件到您的项目中,在需要使用钥匙串的任何地方#import .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];

5
我已经用代码和说明更新了答案。这并不像您想的那么难。
Filip Radelic 2011年

44
吸引力!请补充您的答案,仅“复制KeychainItemWrapper”是不够的!我有一个问题,那就是以后无法构建!您必须将Security.framework添加到KeychainItemWrapper可以使用的项目中!(操作方法:选择项目->选择目标->选择选项卡“构建阶段”->选择“使用库链接二进制文件”->“ +”->添加Security.Framework)
Kovu 2011年

63
使用ARC时,编译器会对使用常量kSecValueDatakSecAttrAccountObjective-C代码大喊大叫,因此请确保使用(__bridge id),例如 [keychainItem setObject:obj forKey:(__bridge id)kSecValueData];
Joe Hankin

7
KeychainItemWrapper.m似乎在第196行出现内存泄漏。将该行更改为“ self.keychainItemData = [[[[NSMutableDictionary alloc] init] autorelease];”。解决它。
Olof

12
使用ARC时,Apple 已在此处提供了更新的代码。看清单2-1。尽管方法是相同的。
瑞克2014年


48

通过钥匙串非常简单的解决方案。

它是系统钥匙串的简单包装。就在增加SSKeychain.hSSKeychain.mSSKeychainQuery.hSSKeychainQuery.m文件到您的项目,并添加Security.framework你的目标。

要保存密码:

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

要获取密码:

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

setPassword您要保存在哪里的值forService是什么,您要保存在哪个帐户和帐户下的变量是密码和其他信息所针对的用户/对象。


您知道如何使用sskeychain来同步具有相同用户名和密码的应用程序吗?
Joe Shamuraq

4
除了密码,您还如何存储用户名?如何从SSKeychain删除整个帐户?两者均未提及文档
user798719 2013年

2
要获取用户名,请执行NSString *username = [[SSKeychain accountsForService:@"AnyService"][0] valueForKey:@"acct"]。如果您仅使用一个帐户,这应该可以正常工作。与往常一样,一定要访问索引0前检查数组长度
贾里德价格

27

您可以简单地使用NSURLCredential它,它只需两行代码即可将用户名和密码保存在钥匙串中

请参阅我的详细答案


13

我决定回答如何使用Obj-C和ARC在iOS 8中使用钥匙串。

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;

请享用!


11

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

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


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

上面的代码中有一个小错误(顺便说一下,Dave对您的帖子非常有帮助,谢谢)

在保存凭据的部分中,它还需要以下代码才能正常工作。

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

很有可能是因为我们第二次尝试使用相同的凭据(重新)登录,但发现它们已在钥匙串项中分配,因此应用程序崩溃。使用上面的代码,它就像一个魅力。


2

更新此问题:

对于那些使用Swift Checkout的用户,Mihai Costea支持访问组的拖放式swift实现:

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

使用钥匙串之前:存储密码之前请三思。在许多情况下,存储身份验证令牌(例如持久性会话ID)以及电子邮件或帐户名可能就足够了。您可以轻松地使身份验证令牌无效,以阻止未经授权的访问,这要求用户再次在受感染的设备上登录,而又不需要重置密码,而必须在所有设备上再次登录(我们不仅在使用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];
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.