在一些设备上,SSKeychain中存储的CFUUID为空

3

我发布的应用程序利用CFUUIDSSKeychain来识别设备(即使卸载并重新安装应用程序,也保持该ID不变)。

我将这些设备ID保存在服务器上,并最近发现一些用户在同一台真实设备上有几个这样的ID。我唯一能想到的解释是ID未从钥匙链中保存或加载,因此设备会生成一个新的ID。奇怪的是,它在运行相同iOS版本的其他设备上正常工作。

您对可能发生的情况有什么想法吗?

以下是我在 (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 中相关的代码:

NSString* identifier = @"appName";
NSString* serviceName = @"com.company.appName";
NSString *retrieveuuid = [SSKeychain passwordForService:serviceName account:identifier];
if (retrieveuuid == nil) {
    CFUUIDRef theUUID = CFUUIDCreate(NULL);
    CFStringRef string = CFUUIDCreateString(NULL, theUUID);
    CFRelease(theUUID);
    NSString *uuid  = (NSString*) string;
    [SSKeychain setPassword:uuid forService:serviceName account:identifier];
}

编辑:我猜测retrieveuuid == nil并没有按预期工作。在应用程序的后面部分,我注册推送通知,并将推送令牌与此CFUUID一起发送到服务器,使用完全相同的代码行[SSKeychain passwordForService:serviceName account:identifier]读取,但是当它发送到服务器时,它不是nil(因此我可以看到几个具有相同推送令牌的CFUUID)。

编辑2以附加更多实际代码。

AppDelegate.m

NSString* identifier = @"appName";
NSString* serviceName = @"com.company.appName";
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //Creating UUID
    NSString *retrieveuuid = [AppDelegate getDeviceId];
    if (retrieveuuid == nil) {
        CFUUIDRef theUUID = CFUUIDCreate(NULL);
        CFStringRef string = CFUUIDCreateString(NULL, theUUID);
        CFRelease(theUUID);
        NSString *uuid  = (NSString*) string;
        [SSKeychain setPassword:uuid forService:serviceName account:identifier];
    }

    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
     (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}

+ (NSString*) getDeviceId {
    return [SSKeychain passwordForService:serviceName account:identifier];
}

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
    NSString *newToken = [deviceToken description];
    newToken = [newToken stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    newToken = [newToken stringByReplacingOccurrencesOfString:@" " withString:@""];

    _deviceToken = [[NSString alloc] initWithString:newToken];

    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    NSString *user = [prefs objectForKey:@"appNameUsername"];

    if(user && ![user isEqualToString:@""]){
        RestClient *rest = [[RestClient alloc] init];
        rest.delegate = self;
        rest.tag = 2;
        [rest updateToken:newToken ForUsername:user];
        [rest release];
    }
}

RestClient.m

-(void) updateToken:(NSString *)token ForUsername:(NSString *)userName{
    NSArray* info = [NSArray arrayWithObject: [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                               userName, @"Username",
                                               token, @"TokenNo",
                                               [[UIDevice currentDevice].model hasPrefix:@"iPad"] ? @"iPad" : @"iPhone", @"Device",
                                               [UIDevice currentDevice].systemVersion, @"OSVersion",
                                               [AppDelegate getDeviceId], @"DeviceID",
                                               @"updateToken", @"CMD",
                                               nil]];
    [self doAction:info];
}

doAction方法只是将数据发送到服务器,然后回调委托,这部分工作正常。我可以在服务器上看到接收此命令的日志:

"JSONCMD":"[
{   "TokenNo" : "f0d3aa21758350333b7e6315c38_EDIT_257c1838f49c43049f8380ec1ff63", 
    "AppVersion" : "1.0.4",
    "Username" : "user@server.com", 
    "CMD" : "updateToken", 
    "OSVersion" : "7.0.4", 
    "DeviceID" : "9B794E11-6EF7-470C-B319-5A9FCCDAFD2B", 
    "Device" : "iPhone"
}
]  

我看到导致奇怪行为的可能是两个因素,控制器主体中的 NSStrings 和静态的 getDevice 方法。然而,我不明白为什么这在许多设备上可以运行但在其他设备上却失败了。


你能展示一下完整的代码,将你的UUID发送到服务器吗? - samir
@samir 我添加了更多的代码。 - momo
2个回答

7

我遇到过同样的问题,现在我找到了解决方法。该问题出现在用户设置了密码以解锁手机的设备上。在iOS7上,一些代码可以在后台运行(推送通知、后台获取),如果你使用标准可访问性类型将某些内容保存在钥匙串中,当设备被锁定时(并且应用在后台运行),你无法读取它。尝试进行以下设置:

[SSKeychain setAccessibilityType:kSecAttrAccessibleAlways];

在读写钥匙串之前,希望这能有所帮助。


1
根据您在Keychain中存储的数据,kSecAttrAccessibleAlways 在这里存在相当大的安全风险。最低限度,您应该使用 kSecAttrAccessibleAfterFirstUnlock,如果您不需要后台访问Keychain,则应坚持使用 kSecAttrAccessibleWhenUnlocked - Sam Meadley

0
NSString *retrieveuuid = [SSKeychain passwordForService:serviceName account:identifier];
retrieveuuid的值取决于服务(service)和账户(account),服务是固定的,但是账户会不同。我认为你的问题在于一个用户可以在同一设备上拥有多个账户,因此你的代码为每个账户生成了一个UUID。

你的意思是即使我始终指定相同的帐户标识符,设备可能配置了多个帐户?我该如何添加更多帐户以便复制该问题? - momo
1
不,如果您指定相同的标识符,则会有一个设备UUID。您确定标识符始终相同吗? - samir
哦,明白了。我一直在指定相同的账户标识符,它是一个硬编码的NSString。 - momo
1
如果服务和账户是硬编码的字符串,那么错误应该出现在其他地方。你的代码很完美。你应该检查其他地方(例如服务器...)来查看错误。 - samir
1
谢谢。很难猜测发生了什么,因为我的测试设备表现正常,而服务器肯定会接收到相同设备的不同CFUUIDs。 - momo
@momo:你解决了吗?你是如何始终获得相同的UUID的? - RameshIos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接