获取iOS设备唯一标识(UUID)并保存到钥匙串

前言:

1 .为了统计和检测应用的使用数据,几乎每家公司都有获取唯一标识的业务需求,在iOS5以前获取唯一标识,可以获取到系统提供的方法UDID(Unique Device Identifier),后来被出于用户隐私的考虑被Apple官方禁止掉了。于是,大家开始在iOS6中使用 MAC 地址(Medium/Media Access Control) ,后来又被Apple官方在iOS7中禁止掉了。苹果及其国外的IT公司都会比较注重用户隐私,所以今后一但有比较靠谱的获取唯一标示的方法放出,苹果肯定会封堵。

2.**  ** 在非越狱的手机上获取某个硬件信息生成唯一标识,第一只能找到苹果的漏洞,第二就是调用一些私有接口,显然这两条路都比较艰难,并不可持续发展,所以网上大部分的唯一标识都是从操作系统层面获取的,在重置手机系统的时候都会被清除,在系统升级、卸载重装、备份恢复都可以保留,现在本人尚未发现可以使用严格意义上的唯一标识。接下来我想跟大家探讨的是如何通过“合法”的手段来尽量拿到不会轻易发生变化的“唯一标识”。

3.**  **在2013年3月21日苹果已经通知开发者,从2013年5月1日起,访问UIDID的应用将不再能通过审核,替代的方案是开发者应该使用“在iOS 6中介绍的Vendor或Advertising标示符”。

4.MAC地址不能再用来设别设备
**  **还有一个生成iOS设备唯一标示符的方法是使用iOS设备的Media Access Control(MAC)地址。一个MAC地址是一个唯一的号码,它是物理网络层级方面分配给网络适配器的。这个地址苹果还有其他的名字,比如说是硬件地址(Hardware Address)或是Wifi地址,都是指同样的东西。
  有很多工程和框架都使用这个方法来生成唯一的设备ID。比如说ODIN。然而,苹果并不希望有人通过MAC地址来分辨用户,所以如果你在iOS7系统上查询MAC地址,它现在只会返回02:00:00:00:00:00。

5 **  ** 讲真苹果这傲娇的小脾气什么时候能能改改,不过这样 对于用户隐私的保护,确实起到很大作用,而且苹果也没有把路堵死,现在苹果明确的表明你应该使用-[UIDevice identifierForVendor]或是-[ASIdentifierManager advertisingIdentifier]来作为你框架和应用的唯一标示符。坦白的来说,应对这些变化也不是那么的难,见以下代码片段:

NSString *identifierForVendor = [[UIDevice currentDevice].identifierForVendor UUIDString]; 
NSString *identifierForAdvertising = [[ASIdentifierManager sharedManager].advertisingIdentifier UUIDString]; 

但是这样用法有个缺点,就是程序每次被删除以后,获取到的都是新的uuid,并不能实现每个手机的唯一性.
所以下面要讲的就是这个问题的解决办法.

**  **对于这个问题有一个可行的办法,就是把获取到的uuid保存在钥匙串里面,这样即使程序删除重装,获取到的uuid一直是同一个,实现了手机的唯一标识的作用.

保存钥匙串 我们需要用到keychain,除此之外,Code Signing Entitlements的创建方法也不够严谨。下面教大家一种方法,简单快速.

1.新建一个工程,看一下自己的Bundle Id.这个Bundle Id 要和你用真机测试时的证书上面的Bundle Id相匹配。

获取iOS设备唯一标识(UUID)并保存到钥匙串_第1张图片
注意这个Bundle identifier

2.Target - Capabilities - Keychain Sharing - ON

获取iOS设备唯一标识(UUID)并保存到钥匙串_第2张图片
注意圆圈里面的保持一致

这步主要目的是打开Keychain Sharing,将它由灰色状态的OFF改为蓝色状态的ON。

获取iOS设备唯一标识(UUID)并保存到钥匙串_第3张图片
会自动生成这个

左侧的目录会自动生成Entitlements文件,不需要自己创建了。

也就是说,Bundle Identifier、Keychain Sharing的Keychain Groups、Entitlements文件的Keychain Access Groups的第一个元素,它们要保持上图所示的一致性。
设置好了以后可以运行下程序,没问题可以进行下一步。

.传说中的uuid类和keychain类来啦
既然苹果的keychain方法会崩溃而且有些复杂,我们只保存一个uuid的话可以用下面的简单方法:


KeyChainStore.h

#import 

@interface KeyChainStore : NSObject

+ (void)save:(NSString *)service data:(id)data;
+ (id)load:(NSString *)service;
+ (void)deleteKeyData:(NSString *)service;

@end



KeyChainStore.m

//
//  KeyChainStore.m
//  getUUID
//
//  Created by ckl@pmm on 16/9/18.
//  Copyright © 2016年 CKLPronetway. All rights reserved.
//

#import "KeyChainStore.h"

@implementation KeyChainStore

+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
    return [NSMutableDictionary dictionaryWithObjectsAndKeys:
            (id)kSecClassGenericPassword,(id)kSecClass,
            service, (id)kSecAttrService,
            service, (id)kSecAttrAccount,
            (id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
            nil];
}

+ (void)save:(NSString *)service data:(id)data {
    //Get search dictionary
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    //Delete old item before add new item
    SecItemDelete((CFDictionaryRef)keychainQuery);
    //Add new object to search dictionary(Attention:the data format)
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    //Add item to keychain with the search dictionary
    SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}


+ (id)load:(NSString *)service {
    id ret = nil;
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    //Configure the search setting
    //Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    CFDataRef keyData = NULL;
    if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
        @try {
            ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
        } @catch (NSException *e) {
            NSLog(@"Unarchive of %@ failed: %@", service, e);
        } @finally {
        }
    }
    if (keyData)
        CFRelease(keyData);
    return ret;
}

+ (void)deleteKeyData:(NSString *)service {
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    SecItemDelete((CFDictionaryRef)keychainQuery);
}
@end
getUUID.h

#import 

@interface getUUID : NSObject
+(NSString *)getUUID;
@end
getUUID.m

//
//  getUUID.m
//  getUUID
//
//  Created by ckl@pmm on 16/9/18.
//  Copyright © 2016年 CKLPronetway. All rights reserved.
//

#import "getUUID.h"
#import "KeyChainStore.h"
#define  KEY_USERNAME_PASSWORD @"com.company.app.usernamepassword"
#define  KEY_USERNAME @"com.company.app.username"
#define  KEY_PASSWORD @"com.company.app.password"

@implementation getUUID

+(NSString *)getUUID
{
    NSString * strUUID = (NSString *)[KeyChainStore load:@"com.company.app.usernamepassword"];
    
    //首次执行该方法时,uuid为空
    if ([strUUID isEqualToString:@""] || !strUUID)
    {
        //生成一个uuid的方法
        CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
        
        strUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString (kCFAllocatorDefault,uuidRef));
        
        //将该uuid保存到keychain
        [KeyChainStore save:KEY_USERNAME_PASSWORD data:strUUID];
        
    }
    return strUUID;
}

@end

在Viewcontroller里面执行如下代码

#import "ViewController.h"
#import "getUUID.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@" uuid  is  ---> %@",[getUUID getUUID]);
    
    // Do any additional setup after loading the view, typically from a nib.
}

打印出来类似于以下的长串字符:

获取iOS设备唯一标识(UUID)并保存到钥匙串_第4张图片
获取到的uuid.png

把程序卸载掉然后重新运行一次,获取到的还是上次保存的uuid.
不知道手机越狱以后,会不会改变,因为楼主手机版本是最新的9.3.5,身边还没相关越狱设备,希望大家可以自行测试一下.并告知楼主.联系方式 : QQ :576484150

参考地址:
:http://blog.sina.com.cn/s/blog_5971cdd00102vqgy.html
:http://blog.csdn.net/chy555chy/article/details/51628079

自己写了一个demo,已经放在gitHub上了,有兴趣的同学可以下载下来瞅瞅.
demo 地址 :https://github.com/chengkunlun/getUUID

你可能感兴趣的:(获取iOS设备唯一标识(UUID)并保存到钥匙串)