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

Posted

技术标签:

【中文标题】iOS:如何在应用程序中存储用户名/密码?【英文标题】:iOS: How to store username/password within an app? 【发布时间】:2011-10-21 18:34:37 【问题描述】:

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

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

所以NSUserDefaults 将被清除。

但是在我的应用程序中,我需要这个用户名/密码来为用户进行数据库查询。 那么:除了NSUserDefaults 之外的数据存储在哪里? (这个地方可以/应该在用户退出应用程序或注销时删除)。

【问题讨论】:

用户只能通过重置设备或删除应用程序来清除它。我错过了什么吗? 顺便说一句,如果数据应该在用户退出应用程序时被删除,为什么不将其保留在 RAM 中? 你应该认真考虑使用 Keychain 来存储用户名和密码,而不是 NSUserDefaults。 你可以从here获得关于swift3实现的基本思路 请问我应该始终使用 kSecValueData 和 kSecValueData 作为键吗?或者我可以使用任何字符串作为键吗? 【参考方案1】:

如果您的应用有关联的域,最好使用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...?!

【讨论】:

【参考方案2】:

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

查看我的详细信息answer。

【讨论】:

【参考方案3】:

我决定回答如何使用 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;

享受吧!

【讨论】:

【参考方案4】:

你可以使用这个库来快速运行:

https://github.com/jrendel/SwiftKeychainWrapper

它支持所有版本的swift。

【讨论】:

【参考方案5】:

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

【讨论】:

【参考方案6】:

Keychains 的一个非常简单的解决方案。

它是系统钥匙串的简单包装器。只需将 SSKeychain.hSSKeychain.mSSKeychainQuery.hSSKeychainQuery.m 文件添加到您的项目中,并将 Security.framework 添加到您的目标中。

保存密码:

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

要找回密码:

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

setPassword 是您希望保存的值,forService 是您希望将其保存在哪个变量下,而 account 是用于密码和任何其他信息的用户/对象。

【讨论】:

你知道如何使用 sskeychain 来同步相同用户名和密码的应用吗? 除了密码之外,您还如何存储用户名?如何从 SSKeychain 中删除整个帐户?两者都没有提到文档 要获取用户名,请执行NSString *username = [[SSKeychain accountsForService:@"AnyService"][0] valueForKey:@"acct"]。如果您只使用一个帐户,这应该可以正常工作。与往常一样,请务必在尝试访问索引 0 之前检查数组长度。【参考方案7】:

更新这个问题:

对于那些使用 Swift 的用户,请查看 Mihai Costea 支持访问组的拖放式快速实现:

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

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

【讨论】:

【参考方案8】:

以下应该可以正常工作:

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

【讨论】:

【参考方案9】:

上面的代码有一个小错误(顺便说一句,Dave 对你的帖子很有帮助,谢谢)

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

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

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

【讨论】:

【参考方案10】:

我查看了使用 KeychainItemWrapper(ARC 版本),但我没有发现它的 Objective C 包装器符合预期。

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

【讨论】:

【参考方案11】:

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

Apple 提供了 sample code 来存储、读取和删除钥匙串项,这里是如何使用该示例中的钥匙串包装类,这极大地简化了钥匙串的使用。

包含 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];

【讨论】:

我已经用代码和描述更新了我的答案。这并不像你想象的那么难。 ATTANTION! 请添加到您的答案中,仅“复制 KeychainItemWrapper”是不够的!我遇到了问题,之后我无法构建它!您必须将 security.framework 添加到 KeychainItemWrapper 才能工作的项目中! (操作方法:选择项目 -> 选择目标 -> 选择选项卡“构建阶段” -> 选择“使用库链接二进制文件” -> “+” -> 添加 Security.Framework) 使用 ARC 时,编译器会因为你在 Objective-C 代码中使用常量 kSecValueDatakSecAttrAccount 而大喊大叫,所以一定要使用 (__bridge id) 进行转换,例如,@987654330 @ KeychainItemWrapper.m 似乎在第 196 行有内存泄漏。将行更改为“self.keychainItemData = [[[NSMutableDictionary alloc] init] autorelease];”修复它。 使用 ARC 时,Apple 已提供更​​新的代码here。请看清单 2-1。虽然方法是一样的。【参考方案12】:

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

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

【讨论】:

【参考方案13】:

试试这个:

 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】:

查看此sample code 我首先从示例代码中尝试了苹果的包装器,但这对我来说要简单得多

【讨论】:

【参考方案15】:

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

【讨论】:

谢谢,我从那个 URL 得到 KeychainItemWrapper .h & .m。

以上是关于iOS:如何在应用程序中存储用户名/密码?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS 应用中管理帐户?

存储用户凭据 iOS 的推荐方式?

将帐户存储到密码和帐户(iOS 设置)

iOS 开发中用户记住账户,密码

如何在ios钥匙串中手动存储?

存储用户数据 iOS