【问题标题】:Uniquely identifying an iOS user唯一标识 iOS 用户
【发布时间】:2012-07-22 01:18:01
【问题描述】:

我想在服务器上为应用程序的新用户创建一个用户帐户,但我也不想要求用户输入任何内容。理想情况下,我希望它是自动的,就像 Game Center 一样。

但我想知道这是否可能。有什么可以用来唯一标识用户的吗?我很难找到用户的 Apple ID。另外,设备 ID 唯一标识的是设备,而不是用户,所以如果用户有更多的设备就没有用了...

还有什么我可以用的吗?

关于隐私 - 我不想在用户背后发现任何事情。我要求用户访问他们的信息绝对没有问题(如果有一个 API 可以授予我这些信息,那么如果 API 自己提出这个问题,那就太好了)。正如史蒂夫·乔布斯本人所说,这就是隐私的全部意义所在——强制应用在对其私人数据进行任何操作之前征求用户的许可。

【问题讨论】:

  • 如果有办法我会感到惊讶.. 它会导致严重的隐私问题。
  • @bdares,我不介意请求用户的许可...“我可以使用您的电子邮件登录吗?”比“输入您的电子邮件并输入密码”要好得多,尤其是在 iPhone 上...
  • 您可以生成一个 UUID(通用唯一标识符),这在 iOS 5 之前使用一些 Core Foundation 功能很容易做到,但 Apple 现在已弃用此功能,因此您无法使用此功能您正在为 iOS 5+ 进行开发。
  • 我不确定这是否可行,但是如何生成随机用户 ID,将其保存到 docs 目录,并由 iCloud 备份?这应该将文件同步到用户的其他设备。当然,这看起来很 hacky,只适用于较新版本的 iOS...
  • @borrrden,这听起来不错,但用户可能不会使用 iCloud。

标签: ios authentication


【解决方案1】:

正确的解决方案是使用iCloud Key-Value Store,您可以在其中存储唯一的用户 ID,而无需任何类型的身份验证或电子邮件地址等用户信息。

最终结果是一个随机的 UUID(实际上并不能识别用户),每个用户都不同,但会在注册到同一个 iCloud 帐户的多个设备上持续存在。

我们在 iCloud KV Store 中定义了一个字段,我们称之为 userID。当应用程序启动时,我们首先检查用户 ID。如果它在那里,那么我们都设置了我们用户的唯一 ID。如果不是,那么这是我们第一次为这个用户运行。我们生成一个随机的 UUID 并将其存储在 userID 下的 KV Store 中。仅此而已。

我们的经验表明,此 UUID 对于每个 iTunes 帐户都是唯一的。如果您的最终用户正在使用家庭共享,则这些帐户将被分配不同的 UUID(这可能是可取的,也可能不是可取的,但您无能为力)。在同一 iTunes 帐户下启动的任意数量的设备都会看到相同的 UUID。

这种方法是完全合法的,应该得到 Apple 的批准,没有任何问题。

显然你必须在 Xcode 的 Capabilities 下启用 iCloud Key-Value store,只需打开 iCloud Switch 即可。

这是一个在 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];

享受吧。

【讨论】:

  • stackoverflow.com/a/11687379/188108 说“如果 iDevice 完全恢复,钥匙串也将被擦除”。我是否正确认为 icloud 中的 UUID 不受此影响?
  • 没错。这不会受到完整设备恢复的影响,因为 UUID 未存储在钥匙串中,而是存储在 iCloud 中。显然,如果用户转到 iCloud 并删除您的特定应用程序的数据,UUID 将会消失,但设备恢复或删除然后重新安装您的应用程序不会重置此 UUID。
  • 这还能用吗?您是否必须考虑在未登录 iCloud 帐户的设备上运行您的应用程序?看起来 iCloud 商店部分可能只是静默失败,而您每次通话都会得到不同的 UUID?
  • 对于任何有同样问题的人......来自文档:“如果您在用户未登录 iCloud 帐户时写入键值存储对象,则数据将存储在本地直到下一个同步机会。当用户登录 iCloud 帐户时,系统会自动将您的本地磁盘密钥和值与 iCloud 服务器上的进行核对。”
【解决方案2】:

用这个生成一个 UUID:

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

Apple 不赞成或反对这里的任何内容 - 事实上,这是他们建议您这样做的方式。将生成的 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 使用[MCSMApplicationUUIDKeychainItem applicationUUID] 为 UUID 生成和存储提供了很好的解决方案。图书馆还有[MCSMGenericKeychainItem genericKeychainItemWithService:service username:username password:password]。这些功能一起处理上述所有内容。使用CocoaPods 也可以轻松安装。

【讨论】:

  • 您是否错过了问题的这一部分:“另外,设备 ID 唯一标识的是设备,而不是用户,所以如果用户有更多的设备......”就没有用了?跨度>
  • 您是否错过了我解决此问题的部分,“然后您可以使用各种方案让用户将他们的设备组合在一起——iCloud,或者您从服务器提供的某种密钥?”
  • 两种可能的方式 1) 在 iCloud 中收集 UUID。让 iCloud 告诉您哪些设备 (uuid) 与用户相关联(这正是 iCloud 的用途)。 2) 在一个设备上向用户发送一个短密钥,并让用户在其他设备上输入它。 1) 无需用户参与即可工作。 2) 需要一些用户参与。
  • 确实可以。也许混合会更好,因为我认为服务器不关心正在使用哪个设备。将第一个 UUID 备份到 iCloud 并使用它来登录(如果不存在则创建它,如果存在则使用它)。我只是担心同步问题(如果 iCloud 在用户尝试访问服务器之前没有同步怎么办?)。但是,我已经看到了曙光,并意识到您的回答不值得我的上述评论。
  • iCloud 方法有点棘手。如果您只需处理一点用户参与,我认为短键就是要走的路。和平。
【解决方案3】:

拉杜

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

Urban Airship 的一大优点是它能够通过电子邮件地址识别“用户”,而不是设备,而是“用户”。它并不是真正的广告“功能”......更多的是其预期功能的副产品。

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

当用户安装您的集成了 Urban Airship 的应用时,UA 会以某种方式生成一个类似 UDID 的编号,该编号目前仅用于识别设备。

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

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

我们已经实际实施并且效果很好!

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

【讨论】:

  • 但这正是我想要避免的——让用户输入他们的电子邮件地址(或输入任何内容)。我希望能够在没有用户干预的情况下识别用户,而不是授予应用某种权限,这只需要按下一个大按钮。
  • 那么您的要求是将多个设备与单个用户 ID 相关联,而无需用户提供任何标识?您可能必须自定义开发其他东西或调整 UA 之类的东西以满足您的需求。您对上述帖子的评论提到您可以要求发送电子邮件。这似乎很合适。这是给我们的。最终,我们决定输入一个电子邮件地址来提供跨平台用户识别是一个简单的权衡。我们也没有任何用户反对。
  • 当然,我完全赞成自定义开发或调整某些东西,如果这会产生预期的结果。我从来没有说过我可以要求电子邮件。我说我可以询问我是否可以使用用户的电子邮件,前提是用户的许可可以让我访问他的电子邮件......理想情况下,我想做一些类似游戏中心的事情,用户自动登录应用程序,无需他的任何努力。
  • 对不起,拉杜...我误解了您的回复。祝你的解决方案好运,很抱歉我帮不上忙。
【解决方案4】:

像 Erik 建议的那样生成 UUID 绝对是一个可靠的解决方案。唯一的(小)警告可能是,在完全恢复 iDevice 的情况下,钥匙串也将被擦除。在这种情况下,应用程序将生成一个新的 UUID,并且 iDevice 无法再与现有用户相关联。

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

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

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

【讨论】:

  • 我不想唯一标识 iDevice。我想唯一标识用户。
  • 我理解您的要求,但是假设几乎不可能实现唯一的用户标识,这至少是一种可能的探索替代方案。
  • 这是一个很好的方法 Bas。我想知道苹果会允许访问 MAC 地址多长时间。 Apple 弃用了旧的 UUID 以防止跨应用程序跟踪设备——我怀疑他们最终可能也会对 MAC ID 做同样的事情。
【解决方案5】:

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 错误

【讨论】:

    【解决方案6】:

    Erik 建议的替代方案是名为OpenUDID 的第三方库。您可以为每个设备生成一个唯一标识符,这非常简单:

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

    【讨论】:

      【解决方案7】:

      游戏中心和iCloud一起使用如何识别用户?

      用户不玩游戏且不同步数据的可能性较小。

      【讨论】:

      • 如果您将其用于非游戏应用程序,您将被苹果商店拒绝。
      【解决方案8】:

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

      【讨论】:

      • 我已经多次明确表示这不是我想要的。我知道如何识别设备。我不在乎识别设备。
      【解决方案9】:

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

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

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

      【讨论】:

        【解决方案10】:

        今天我们的APP被拒绝了,因为Game Center用户识别。它在 App Store 指南中:https://developer.apple.com/appstore/resources/approval/guidelines.html

        您不能使用 G: 格式的唯一游戏中心 ID。

        我们现在唯一的方法是使用 iOS6 Facebook 集成。

        更新:我们带有自动 FB 登录功能的新版本通过了审核流程,可在 App Store (Slovni Duel) 上找到。也使用链接到 FB 个人资料的应用内订阅。

        【讨论】:

          猜你喜欢
          • 2011-11-08
          • 2021-10-06
          • 1970-01-01
          • 1970-01-01
          • 2023-03-22
          • 1970-01-01
          • 1970-01-01
          • 2014-04-05
          相关资源
          最近更新 更多