独特地识别iOS用户

41

我想在服务器上为新用户创建一个用户帐户,但我不想要求用户输入任何信息。理想情况下,我希望这是自动的,就像使用Game Center一样。

但我想知道是否可能。有没有什么东西可以用来唯一地识别用户?我几乎不可能找到用户的Apple ID。而设备ID只能唯一标识设备,而不能标识用户,所以如果用户有更多的设备,它将无用...

还有其他可以使用的方法吗?

关于隐私 - 我不想在用户背后发现任何事情。我完全没有问题向用户请求访问其信息(如果有授予我此信息的API,则最好API本身会要求此权限)。正如史蒂夫·乔布斯本人所说,这就是隐私的全部内容 - 强制应用程序在使用用户的私人数据之前征得用户的许可。


5
如果有一种方法的话,我会感到惊讶...它会导致严重的隐私问题。 - user684934
@bdares,我不介意向用户请求权限...“我可以使用您的电子邮件登录吗?”比“输入您的电子邮件并设置密码”更好,特别是在iPhone上... - rid
1
我不确定这是否有效,但是我们可以考虑生成一个随机用户ID,将其保存到文档目录中,并由iCloud备份。这样应该可以将文件同步到用户的其他设备。当然,这似乎非常“hacky”,且只能在较新版本的iOS上运行... - borrrden
@Erik,我不明白它如何帮助我,如果用户不使用iCloud的话。我曾经有过类似的想法,使用Game Center,但是同样的问题是,用户可能没有Game Center账户。而且从一个设备发送密钥到另一个设备听起来也不太好。理想情况下,我希望不打扰用户,也不需要他采取任何行动。自动登录Game Center的方式是我正在寻找的理想解决方案。 - rid
@Erik,我使用iCloud没有问题,但用户可能没有iCloud帐户或根本不使用iCloud。iCloud是可选的。关键系统并不理想,但也不是那么糟糕...然而,用户仍然必须拥有该应用程序。如果他们删除了应用程序,则帐户将丢失。我希望他们能够在删除所有应用程序副本并重新安装后继续使用帐户。 - rid
显示剩余15条评论
10个回答

34
正确的解决方案是使用iCloud键值存储,您可以在不需要任何身份验证或用户信息(如电子邮件地址)的情况下存储唯一的用户ID。
最终结果是一个随机UUID(并未真正标识用户),对于每个用户都是不同的,但将持久存在于注册到相同iCloud帐户的多个设备上。
我们在iCloud KV存储中定义一个字段,称为userID。当应用程序启动时,我们首先检查userID。如果有了,那么我们就有了用户的唯一ID。如果没有,那么这是我们为此用户运行的第一次。我们生成一个随机UUID并将其存储在userID下的KV Store中。就是这样。
我们的经验表明,此UUID对于每个iTunes帐户都是唯一的。如果您的最终用户正在使用家庭共享,则这些帐户将被分配不同的UUID(可能是期望的,也可能不是),但您无法做任何事情。任何数量的设备在相同的iTunes帐户下启动将看到相同的UUID。
这种方法是完全合法的,应该没有任何问题得到Apple的批准。
显然,您必须在Xcode的功能下启用iCloud键值存储,只需打开iCloud开关即可。
这是一个简单的Objective-C类,实现了这个概念:
@implementation EEUserID

+ (NSUUID *) getUUID
{
    NSUUID *uuid = nil;
    NSString *uuidString = [[NSUbiquitousKeyValueStore defaultStore] stringForKey: @"EEUserID"];
    if (uuidString == nil)
    {
        // This is our first launch for this iTunes account, so we generate random UUID and store it in iCloud:
        uuid = [NSUUID UUID];
        [[NSUbiquitousKeyValueStore defaultStore] setString: uuid.UUIDString forKey: @"EEUserID"];
        [[NSUbiquitousKeyValueStore defaultStore] synchronize];
    }
    else
    {
        uuid = [[NSUUID alloc] initWithUUIDString: uuidString];
    }
    
    return uuid;
}

+ (NSString *) getUUIDString
{
    NSUUID *uuid = [self getUUID];
    if (uuid != nil)
        return uuid.UUIDString;
    else
        return nil;
}

+ (void) load
{
    // get changes that might have happened while this
    // instance of your app wasn't running
    [[NSUbiquitousKeyValueStore defaultStore] synchronize];
}

@end

而对于头文件:

#import <Foundation/Foundation.h>

@interface EEUserID : NSObject

+ (NSUUID *) getUUID;
+ (NSString *) getUUIDString;

@end

要使用,您所要做的就是调用:

NSString *uniqueIDForiTunesAccount = [EEUserID getUUIDString];

享受。


https://dev59.com/5Wgu5IYBdhLWcg3wFDJr#11687379提到“在进行完整的iDevice恢复时,钥匙串也会被清除”。那么我是否正确地理解iCloud中的UUID不受此影响呢? - Torsten Bronger
1
这是正确的。全设备恢复不会受到影响,因为UUID不存储在钥匙串中,而是存储在iCloud中。显然,如果用户转到iCloud并删除您特定应用程序的数据,则UUID将消失,但设备恢复或删除然后重新安装您的应用程序将不会重置此UUID。 - ldoogy
这还能用吗?你是否需要考虑到你的应用在未登录iCloud帐户的设备上运行的情况?看起来iCloud存储部分可能会悄悄地失败,每次调用时你都会得到不同的UUID? - Chad Podoski
对于任何有同样问题的人...从文档中可以得知:"如果在用户未登录iCloud帐户时写入键值存储对象,则数据将本地存储,直到下一次同步机会。当用户登录iCloud帐户时,系统会自动将您的本地磁盘上的键和值与iCloud服务器上的键和值进行协调。" - Chad Podoski

21

使用以下代码生成 UUID:

NSString *UUID() {
    CFUUIDRef cfuuid = CFUUIDCreate(NULL); 
    NSString *uuid =  (__bridge_transfer NSString *)CFUUIDCreateString(NULL, cfuuid); 
    CFRelease(cfuuid);
    return uuid;
}

这里没有任何被苹果弃用或不赞成的内容,事实上,这是他们建议您采用的方式。将生成的UUID存储在钥匙串中,即使用户卸载并重新安装您的应用程序,它也会存在那里。UUID是设备和生成时间的唯一标识。

然后,您可以使用各种方案来让用户将其设备分组- iCloud,或者从服务器传递某种密钥。

祝好运!

补充:

以下是我如何将其存储在钥匙串中,使用UUID作为用户名并生成随机密码:

uuid = UUID();
[keychainItemWrapper setObject:uuid forKey:(__bridge_transfer id)kSecAttrAccount];
NSString *pass_token = randomString(10);
[keychainItemWrapper setObject:pass_token forKey:(__bridge_transfer id)kSecValueData];

请注意,所有这些都可以在没有用户输入的情况下完成。
更新: MCSMKeychainItem 提供了一个很好的 UUID 生成和存储解决方案,使用 [MCSMApplicationUUIDKeychainItem applicationUUID]。该库还有 [MCSMGenericKeychainItem genericKeychainItemWithService:service username:username password:password]。这些函数共同处理了上述所有内容。使用 CocoaPods 安装也非常容易。

1
你有没有错过问题的这一部分:“此外,设备ID唯一地标识设备,而不是用户,因此如果用户拥有更多设备,它将毫无用处…”? - borrrden
15
你是否错过了我在这段话中提到的部分:“然后你可以使用各种方案,让用户将他们的设备组合在一起——iCloud、或者从服务器上提供的某种密钥?”请注意,翻译保持原意但更通俗易懂,不包含解释,并限制返回结果仅为翻译文本。 - Erik
3
有两种可能的方法:1)在iCloud中收集UUID。让iCloud告诉你哪些设备(uuids)与用户相关联(这正是iCloud的作用)。2)向用户的一个设备发送一个短密钥,并要求用户在其他设备上输入它。1)无需用户参与即可运行。2)需要一些用户参与。 - Erik
2
那些方案确实可行。也许采用混合方案会更好,因为我认为服务器不关心使用哪个设备。将第一个UUID备份到iCloud,并使用它进行登录(如果不存在则创建,如果存在则使用)。我只担心同步问题(如果iCloud在用户尝试访问服务器之前还没有同步怎么办?)。然而,我已经看到了光明,意识到您的答案并不值得我的上面评论。 - borrrden
1
iCloud的方法有些棘手。我认为,如果您可以接受一点用户参与,短密钥是比较好的选择。祝平安。 - Erik
显示剩余2条评论

3

Radu

很好的问题。我们通过将Urban Airship(urbanairship.com)集成到我们的应用程序中来解决了您描述的问题。Urban Airship提供了一系列功能,支持应用内购买、收据验证、订阅恢复、内容传递和苹果推送通知。

Urban Airship的一个伟大之处在于它能够通过电子邮件地址识别“用户”,而不是设备。这不是一个宣传的“特性”...更像是其预期功能的副产品。

以下是我们发现的情况以及如何利用Urban Airship解决您的问题。

当用户安装具有Urban Airship集成的应用程序时,UA会生成一个类似UDID的数字,目前仅用于标识设备。

但是,如果您利用Urban Airship的订阅恢复组件,您可以要求用户输入电子邮件地址。当用户在第一台设备上输入其电子邮件地址时,生成的ID将成为其主要的用户标识方法,并与该电子邮件地址相关联。当他们在后续设备上输入其电子邮件地址时,Urban Airship将触发电子邮件验证过程。一旦用户完成验证过程,它将更新新设备上的ID为与第一台设备相同的ID,依此类推。最好的部分是...这一切都是自动的!您只需集成组件并要求用户输入其电子邮件地址。您应该能够在一个小时内全部完成。

它还提供了功能,允许用户更改与其所有设备关联的电子邮件地址。

我们实际上已经实施了它,效果非常好!

注意:从2013年7月1日起,Urban Airship将停用订阅和恢复功能。


2
但这正是我想要避免的 - 让用户输入他们的电子邮件地址(或输入任何内容)。我希望能够在不需要干预用户的情况下识别用户,除了可能授予应用程序某种权限外,这只需要按下一个单独的大按钮即可。 - rid
所以您的要求是将多个设备关联到单个用户ID,而无需用户提供任何身份证明?您可能需要自定义开发其他内容或调整类似UA的内容以满足您的需求。您在上面的帖子中的评论提到您可以要求提供电子邮件。这似乎很合适。对我们来说也是如此。最终,我们决定输入单个电子邮件地址以提供跨平台用户识别是一个简单的权衡。我们也没有遇到任何用户的反对意见。 - radesix
当然,如果这样做可以达到预期的结果,我完全支持定制开发或调整某些内容。但我从未说过我同意要求用户提供电子邮件。我说过,只要用户同意并授权我访问他的电子邮件,我就可以请求使用用户的电子邮件。理想情况下,我希望能够像Game Center一样,在用户不需要任何努力的情况下自动登录应用程序。 - rid
对不起 Radu...我误解了你的回答。祝你的解决方案好运,很抱歉我没能提供帮助。 - radesix

2

Swift3/4+

使用iCloud记录ID(CKRecordID)。该ID将是iTunes账户唯一的。

let container = CKContainer.default()
container.fetchUserRecordID() {
    recordID, error in

    if let err = error {
        print(err.localizedDescription)
    }
    else if recID = recordID {
        print("fetched ID \(recID.recordName ?? "NA")")
    }
}

对于自动创建的记录,ID名称字符串基于UUID并因此保证唯一性。
请注意,如果设备没有iCloud帐户,或者具有受限制的访问权限的iCloud帐户,则会返回CKError.Code.notAuthenticated错误。

1

像Erik建议的那样生成UUID绝对是一个可靠的解决方案。唯一(小)的注意事项可能是,在进行完整的iDevice恢复时,钥匙串也将被清除。在这种情况下,应用程序将生成一个新的UUID,iDevice将无法与现有用户相关联。

另一种独特标识iDevice的方法是基于网络接口的MAC地址生成一个唯一的ID,并将其与应用程序包ID组合。这种方法产生了一个特定于应用程序且持久的唯一ID。当iDevice被擦除、恢复或应用程序被删除并重新安装时,它不会改变。因此,无需将此ID存储在钥匙串中。

幸运的是,已经有一些人创建了一个小型库来实现这一点。代码可以在GitHub上找到:

https://github.com/gekitz/UIDevice-with-UniqueIdentifier-for-iOS-5


1
我不想唯一地识别iDevice。我想唯一地识别用户。 - rid
我理解您的需求,然而在假设无法实现唯一用户标识的情况下,这是至少可以探索的一个可能的替代方案。 - Bas Peters
这是一个很好的方法,Bas。我想知道苹果公司会允许访问 MAC 地址多久。苹果公司已经弃用了旧的 UUID 以防止跨应用程序跟踪设备- 我认为他们最终也可能会采取同样的措施来处理 MAC ID。 - Erik

0

对于Erik的建议,还有一种替代方案是使用一个叫做OpenUDID的第三方库。你可以按设备生成一个唯一的标识符,而且非常简单易用:

#include "OpenUDID.h"
NSString* openUDID = [OpenUDID value];

0

今天我们的APP因为Game Center用户身份验证被拒绝了。这是在App Store指南中规定的:https://developer.apple.com/appstore/resources/approval/guidelines.html

您不能使用G:格式中的唯一Game Center ID。

现在我们唯一的选择是使用iOS6 Facebook集成。

更新:我们的新版本通过了审核流程,并可在App Store上下载(Slovni Duel)。还使用与FB个人资料相关联的inApp订阅。


0

你可以使用第三方解决方案,例如SECUREUDID,还可以查看this


2
我已经明确说过多次,这 不是 我要找的。我知道如何识别设备。我不关心识别设备。 - rid

0

这不是绝对可靠的解决方案(只在启用iCloud时有效),但很容易实现:生成一个唯一的ID(如UUID),并将其存储在iCloud键值存储中。

唯一的问题是高级用户知道如何从iCloud中删除内容。

为了防止用户仅编辑存储在iCloud中的唯一ID以更改为其他人的ID,您可以添加某种秘密。


-1

如何同时使用游戏中心和iCloud来识别用户?

用户很可能会玩游戏并同步他们的数据,所以这种方法是比较可行的。


1
如果您在非游戏应用程序中使用此内容,您将被苹果商店拒绝。 - Martin Berger

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