我的应用程序在用户登录或向上注册时存储用户的凭据。当应用程序启动时,如果我们已经存储了凭据,我就会签入didFinishLaunchingWithOptions。当通过点击app或从Xcode启动应用程序时,这很好。
然而,当应用程序在后台被杀死,系统由于位置更改更新而重新启动时,defaultCredentialForProtectionSpace返回的凭据为零。当我再次重新启动应用程序时,凭证又回来了。
因此,当[launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]为真时,NSURLCredentialStorage返回的NSURLCredential为零;当它为假时,我们得到预期的凭据。
下面是一些代码:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Other init things happen here, including setting up the NSURLProtectionSpace
NSURLCredentialStorage *credentialStorage = [NSURLCredentialStorage sharedCredentialStorage];
NSURLCredential *credential = [credentialStorage defaultCredentialForProtectionSpace:self.protectionSpace];
if (credential) {
// do something - XXX this does not happen when app is launched in background
}
} 发布于 2014-05-22 21:32:14
结果是,NSURLCredentialStorage在密钥链中设置kSecAttrAccessibleWhenUnlocked来存储凭据。这意味着当用户设置密码以解锁手机和手机被锁定时,无法访问凭据。
编辑
上面解释了为什么它不起作用,下面是我最终实现的解决方案:将密钥链中的kSecAttrAccessible更改为kSecAttrAccessibleAfterFirstUnlock,并处理在手机重新启动但尚未解锁的罕见情况下无法访问凭据带来的问题。
当应用程序处于前台时,密钥链将在NSURLCredentialStorage:kSecAttrAccessibleWhenUnlocked使用的模式中解锁。因此,在applicationDidBecomeActive:中,我们可以从NSURLCredentialStorage访问密钥链条目并进行更改:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"server.com" accessGroup:nil];
[wrapper setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge id)kSecAttrAccessible];
// ...
}上面的代码使用了KeychainItemWrapper的改编版本。最初的版本来自苹果,但我的解决方案是基于ARCified版本的:https://gist.github.com/dhoerl/1170641。您仍然需要更改该版本以使用kSecClassInternetPassword而不是kSecClassGenericPassword,这主要意味着您不能使用kSecAttrGeneric查询密钥链项,而只能使用kSecAttrServer。
编辑
上述方法在iOS 7.x中运行良好,但在iOS 8.x上不再工作。代码运行良好,但密钥链仍然使用kSecAttrAccessibleWhenUnlocked。
现在唯一的解决方案是,只有当您知道只在前台使用NSURLCredentialStorage时才使用它。否则,您需要使用来自KeychainItemWrapper的https://gist.github.com/dhoerl/1170641将凭证存储在密钥链中。然后,您可以在存储凭据时自己设置kSecAttrAccessibleAfterFirstUnlock。
示例代码:
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"YOUR_IDENTIFIER accessGroup:nil];
[keychain setObject:(__bridge id)kSecAttrAccessibleWhenUnlocked forKey:(__bridge id)kSecAttrAccessible];
[keychain setObject:userName forKey:(__bridge id)kSecAttrAccount];
[keychain setObject:[password dataUsingEncoding:NSUTF8StringEncoding] forKey:(__bridge id)kSecValueData];https://stackoverflow.com/questions/23815560
复制相似问题