iOS UDID/UUID/.../MAC Address/Keychain

近期開發(fā)中有一個生成設(shè)備指紋的需求姻氨,什么是設(shè)備指紋呢?簡單講剪验,就是設(shè)備的唯一標識肴焊,用來標識某一設(shè)備。那么這個值功戚,在通常情況下娶眷,應(yīng)該是唯一且不可變的。iOS 系統(tǒng)提供了一些字段啸臀,但是單一使用這些字段并不能完全滿足需求届宠,下面對這些字段做些介紹。

一乘粒、UDID

1豌注、什么是 UDID
UDID 「Unique Device Identifier Description」是由子母和數(shù)字組成的40個字符串的序號,用來區(qū)別每一個唯一的 iOS 設(shè)備灯萍,包括 iPhone轧铁,iPad,以及 iPod touche旦棉,這些編碼看起來是隨機的齿风,實際上是跟硬件設(shè)備特點相聯(lián)系的药薯,另外你可以到 iTunes 等軟件查看設(shè)備的 udid。

2救斑、UDID 可以用來做什么
iOS 2.0 版本以后 UIDevice 提供一個獲取設(shè)備唯一標識符的方法 uniqueIdentifier童本,通過該方法我們可以獲取設(shè)備的序列號,這個也是目前為止唯一可以確認的設(shè)備的唯一標示符脸候。UDID 可以關(guān)聯(lián)其他各種數(shù)據(jù)到相關(guān)設(shè)備上穷娱。例如,連接到開發(fā)者賬號纪他,可以在 APP 發(fā)布前進行真機測試鄙煤;也可以讓開發(fā)者獲得 APP 測試版進行體驗;蘋果用 UDID 連接到蘋果的 ID茶袒,這些設(shè)備可以自動下載和安裝從 App Store 購買的應(yīng)用、保存從 iTunes 購買的音樂凉馆、幫助蘋果發(fā)送推送通知薪寓、即時消息等。 在 iOS 應(yīng)用早期澜共,UDID 被第三方應(yīng)用開發(fā)者和網(wǎng)絡(luò)廣告商用來收集用戶數(shù)據(jù)向叉,可以用來關(guān)聯(lián)地址、記錄應(yīng)用使用習(xí)慣等嗦董,以便推送精準廣告母谎。

3、獲取方法

NSString *udid = [[UIDevice cuurrent] uniqueIdenfier];

4京革、使用限制
許多開發(fā)者把UDID跟用戶的真實姓名奇唤、密碼、住址匹摇、其它數(shù)據(jù)關(guān)聯(lián)起來咬扇;網(wǎng)絡(luò)窺探者會從多個應(yīng)用收集這些數(shù)據(jù),然后順藤摸瓜得到這個人的許多隱私數(shù)據(jù)廊勃。同時大部分應(yīng)用確實在頻繁傳輸 UDID 和私人信息懈贺,為了避免集體訴訟,蘋果最終決定在 iOS 5 的時候坡垫,將這一慣例廢除梭灿,開發(fā)者被引導(dǎo)生成一個唯一的標識符,只能檢測應(yīng)用程序冰悠,其他的信息不提供”ざ剩現(xiàn)在應(yīng)用試圖獲取 UDID 已被禁止且不允許上架。

二屿脐、UUID

1涕蚤、什么是 UUID
UUID 英文名稱是:Universally Unique Identifier宪卿,翻譯過來就是通用唯一標識符。是一個32位的十六進制序列万栅,使用小橫線來連接:F8C6B3D6-0539-40BB-B26A-AD7D49E9C9D3佑钾。UUID 在某一時空下是唯一的。比如在當(dāng)前這一秒烦粒,全世界產(chǎn)生的 UUID 都是不一樣的休溶;當(dāng)然同一臺設(shè)備產(chǎn)生的 UUID 也是不一樣的。

2扰她、獲取方法

+ (void)UUIDString
{
    NSString *uuid = [NSUUID UUID].UUIDString;
    NSLog(@"uuid %@", uuid);
}
// 查看取值結(jié)果
uuid F8C6B3D6-0539-40BB-B26A-AD7D49E9C9D3
uuid 4EE64A18-F928-4FDF-A37D-FFAB5053A799
uuid BA2FADEC-C5D0-47DC-AB5B-09ECFD57532D
uuid 382B879E-9D0A-432B-9BC4-950AEEB36BE5
uuid 771A4087-C5DD-4D26-BB4F-272551B5B412

從打印結(jié)果可以看出兽掰,每次取到的 UUID 值是不一樣的,當(dāng)然徒役,無論取多少次值孽尽,永遠不會出現(xiàn)兩個一樣的結(jié)果。所以從某種程度上來說忧勿,UUID 跟設(shè)備已經(jīng)沒有什么關(guān)系了杉女。

三、IMEI

1鸳吸、什么是 IMEI
IMEI (International Mobile Equipment Identity) 是國際移動設(shè)備身份碼的縮寫熏挎,國際移動裝備辨識碼,是由15位數(shù)字組成的”電子串號”晌砾,它與每臺手機一一對應(yīng)坎拐,而且該碼是全世界唯一的。每一部手機在組裝完成后都將被賦予一個全球唯一的一組號碼养匈,這個號碼從生產(chǎn)到交付使用都將被制造生產(chǎn)的廠商所記錄哼勇。手機用戶可以在手機中查到自己手機的IMEI碼。

2乖寒、使用限制
iOS 2 提供了一種方法獲取 IMEI猴蹂,但是為了保護用戶隱私,iOS 5 以后蘋果不再允許獲取 IMEI 了楣嘁,如果你在應(yīng)用中獲取 IMEI磅轻,可能會遭到 Apple Store 的拒絕。

四逐虚、IDFA

1聋溜、什么是 IDFA
IDFA(Identifier for Identifier),即廣告標識符叭爱,多用于用戶的廣告追蹤撮躁,是每臺設(shè)備的唯一 ID,IDFA 存儲在用戶的系統(tǒng)上买雾。Apple 是不允許開發(fā)者追蹤用戶設(shè)備的把曼,但是為了監(jiān)控廣告效果杨帽,在 iOS 6 中提供這個折中方案,但是如果用戶還原設(shè)置與隱私的話這個廣告標識符就會重新生成(設(shè)置 -> 通用 -> 還原 -> 還原位置與隱私)或者用戶更明確的將設(shè)置中的廣告還原掉(設(shè)置程序 -> 通用 -> 關(guān)于本機 -> 廣告 -> 還原廣告標標識符)IDFA 也會重新生成嗤军。而且用戶可以在設(shè)置 -> 隱私 -> 廣告里面限制 IDFA 獲取注盈。

2、獲取方法

// 需要引入系統(tǒng)框架
import <AdSupport/AdSupport.h>

// 獲取方法
NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];

五叙赚、IDFV

1老客、什么是 IDFV
IDFV(identifierForVendor):是給 Vendor 標識用戶用的,每個設(shè)備在所屬同一個Vender的應(yīng)用里震叮,都有相同的值胧砰。其中的Vender是指應(yīng)用提供商,但準確點說苇瓣,是通過BundleID的反轉(zhuǎn)的前兩部分進行匹配尉间,如果相同就是同一個Vender,例如對于 com.taobao.app1, com.taobao.app2 這兩個 BundleID 來說击罪,就屬于同一個 Vender乌妒,共享同一個 idfv 的值。和 idfa 不同的是外邓,idfv 的值是一定能取到的,所以非常適合于作為內(nèi)部用戶行為分析的主 id古掏,來標識用戶损话,替代 OpenUDID。

注意:如果用戶將屬于此 Vender 的所有 App 卸載槽唾,則 idfv 的值會被重置丧枪,即再重裝此 Vender 的 App,idfv 的值和之前不同庞萍。

2拧烦、獲取方法

NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

IDFV 比較適合追蹤用戶行為,目前多配合 Keychain 來搭配使用钝计,來保證不管是否卸載重裝恋博,IDFV 都是不變的。

六私恬、MAC 地址

1债沮、什么是 MAC 地址
MAC(Medium/Media Access Control)地址,用來表示互聯(lián)網(wǎng)上每一個站點的標識符本鸣,采用十六進制數(shù)表示疫衩,共六個字節(jié)(48位)。其中荣德,前三個字節(jié)是由IEEE的注冊管理機構(gòu) RA負責(zé)給不同廠家分配的代碼(高位24位)闷煤,也稱為“編制上唯一的標識符” (Organizationally Unique Identifier)童芹,后三個字節(jié)(低位24位)由各廠家自行指派給生產(chǎn)的適配器接口,稱為擴展標識符(唯一性)鲤拿。

MAC 地址在網(wǎng)絡(luò)上用來區(qū)分設(shè)備的唯一性假褪,接入網(wǎng)絡(luò)的設(shè)備都有一個 MAC 地址,他們肯定都是不同的皆愉,是唯一的嗜价。一部iPhone上可能有多個 MAC 地址,包括 WIFI 的幕庐、SIM 的等久锥,但是 iTouch 和 iPad 上就有一個 WIFI 的,因此只需獲取 WIFI 的 MAC 地址就好了异剥,也就是en0的地址瑟由。

形象的說,MAC 地址就如同我們身份證上的身份證號碼冤寿,具有全球唯一性歹苦。這樣就可以非常好的標識設(shè)備唯一性,類似與蘋果設(shè)備的 UDID 號督怜,通常的用途有:1)用于一些統(tǒng)計與分析目的,利用用戶的操作習(xí)慣和數(shù)據(jù)更好的規(guī)劃產(chǎn)品号杠;2)作為用戶 ID 來唯一識別用戶蚪腋,可以用游客身份使用 APP 又能在服務(wù)器端保存相應(yīng)的信息,省去用戶名姨蟋、密碼等注冊過程屉凯。

但是,從 iOS7 及更高版本往后眼溶,如果你向 iOS 設(shè)備請求獲取 MAC 地址悠砚,系統(tǒng)將返回一個固定值 “02:00:00:00:00:00”,因為 MAC 地址跟 UDID 一樣堂飞,存在隱私問題灌旧。

2、獲取方法

// 引入一下庫文件
#import <sys/sysctl.h>  
#import <net/if.h>  
#import <net/if_dl.h>

// 實現(xiàn)代碼
+ (NSString *)getMacAddress {

    int    mib[6];
    size_t len;
    char   *buf;
    unsigned char      *ptr;
    struct if_msghdr   *ifm;
    struct sockaddr_dl *sdl;
    
    mib[0] = CTL_NET; mib[1] = AF_ROUTE;
    mib[2] = 0; mib[3] = AF_LINK;
    mib[4] = NET_RT_IFLIST;
    
    if ((mib[5] = if_nametoindex("en0")) == 0) {
        printf("Error: if_nametoindex error/n");
        return NULL;
    }
    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 1/n");
        return NULL;
    }
    if ((buf = malloc(len)) == NULL) {
        printf("Could not allocate memory. error!/n"); return NULL;
    }
    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
        free(buf);
        printf("Error: sysctl, take 2");
        return NULL;
    }
    
    ifm = (struct if_msghdr *)buf;
    sdl = (struct sockaddr_dl *)(ifm + 1);
    ptr = (unsigned char *)LLADDR(sdl);
    
    // MAC 地址
    NSString *outstring = [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
    
    free(buf);
    
    return [outstring uppercaseString];
}

七酝静、Keychain

為什么這里要提到 Keychain 呢节榜,因為上面介紹的所有字段,目前都不能完全實現(xiàn)對設(shè)備的唯一標識别智,因此宗苍,需要換種方式來滿足需求,即 IDFV + Keychain 的形式。

1讳窟、什么是 Keychain
Keychain Services 是 OS X 和 iOS 都提供一種安全地存儲敏感信息的工具让歼,比如,存儲用戶ID丽啡,密碼谋右,和證書等。存儲這些信息可以免除用戶重復(fù)輸入用戶名和密碼的過程补箍。Keychain Services 的安全機制保證了存儲這些敏感信息不會被竊取改执。

2、Keychain 的結(jié)構(gòu)
Keychain 可以包含任意數(shù)量的 keychain item坑雅。每一個 keychain item 包含數(shù)據(jù)和一組屬性辈挂。對于一個需要保護的 keychain item,比如密碼或者私鑰(用于加密或者解密的 string 字節(jié))會被 keychain 保護起來裹粤,數(shù)據(jù)是加密的终蒂;對于無需保護的 keychain item,例如遥诉,證書拇泣,數(shù)據(jù)不會被加密。

item可以指定為以下的類型:

extern CFTypeRef kSecClassGenericPassword 
extern CFTypeRef kSecClassInternetPassword
extern CFTypeRef kSecClassCertificate 
extern CFTypeRef kSecClassKey 
extern CFTypeRef kSecClassIdentity OSX_AVAILABLE_STARTING(MAC_10_7, __IPHONE_2_0);

3矮锈、Keychain 的特點

  • 數(shù)據(jù)并不存放在 App 的 Sanbox 中霉翔,即使刪除了 APP,資料依然保存在 keychain 中苞笨。如果重新安裝了APP早龟,還可以從 keychain 獲取數(shù)據(jù)。
  • keychain 的數(shù)據(jù)可以用過 group 方式猫缭,讓程序可以在 APP 間共享,不過得要相同 TeamID壹店。
  • keychain 的數(shù)據(jù)是經(jīng)過加密的猜丹。

4、Keychain 的使用
當(dāng)你想使用 keychain 進行一些操作硅卢,如保存一個密碼射窒,修改一個已存在 Keychain item 或者取回密碼等。Keychain 可以提供以下的操作:

  • SecItemAdd 添加一個 item将塑;
  • SecItemUpdate 更新已存在的 item脉顿;
  • SecItemCopyMatching 搜索一個已存在的 item;
  • SecItemDelete 刪除一個 keychain item点寥;

<1> 根據(jù)特定的 Service 創(chuàng)建一個用于操作 KeyChain 的 Dictionary

+ (NSMutableDictionary *)keyChainQueryDictionaryWithService:(NSString *)service 
{ 
    NSMutableDictionary *keyChainQueryDictaionary = [[NSMutableDictionary alloc]init];
    [keyChainQueryDictaionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; 
    [keyChainQueryDictaionary setObject:service forKey:(id)kSecAttrService]; 
    [keyChainQueryDictaionary setObject:service forKey:(id)kSecAttrAccount];
 
    return keyChainQueryDictaionary; 
}

<2> 添加數(shù)據(jù)

+ (BOOL)addData:(id)data forService:(NSString *)service 
{ 
    NSMutableDictionary *keychainQuery = [self keyChainQueryDictionaryWithService:service]; 
    SecItemDelete((CFDictionaryRef)keychainQuery);
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData]; 
    OSStatus status= SecItemAdd((CFDictionaryRef)keychainQuery, NULL); 
    
    if (status == noErr) { 
        return YES;
     } 
    return NO; 
}

<3> 搜索數(shù)據(jù)

+ (id)queryDataWithService:(NSString *)service 
{ 
    id result; 
    NSMutableDictionary *keyChainQuery = [self keyChainQueryDictionaryWithService:service]; 
    [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 { 
            result = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData]; 
        } 
        @catch (NSException *exception) { 
            NSLog(@"不存在數(shù)據(jù)"); 
        } 
        @finally {
        } 
    }

    if (keyData) { 
        CFRelease(keyData); 
    } 
    return result; 
}

<4> 更新數(shù)據(jù)

+ (BOOL)updateData:(id)data forService:(NSString *)service
{
    NSMutableDictionary *searchDictionary = [self keyChainQueryDictionaryWithService:service]; 
    if (!searchDictionary) { 
        return NO; 
    } 

    NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init]; 
    [updateDictionary setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary, (CFDictionaryRef)updateDictionary);
    if (status == errSecSuccess) { 
        return YES; 
    } 
    return NO; 
}

<5> 刪除數(shù)據(jù)

+ (BOOL)deleteDataWithService:(NSString *)service
{
    NSMutableDictionary *keyChainDictionary = [self keyChainQueryDictionaryWithService:service]; 
    OSStatus status = SecItemDelete((CFDictionaryRef)keyChainDictionary); 

    if (status == noErr) { 
       return YES; 
    } 
    return NO; 
}

八艾疟、結(jié)語

到目前為止,想要僅僅通過 UUID、UDID蔽莱、Mac 地址等弟疆,來定位用戶設(shè)備已經(jīng)無法滿足需求,因為它們要么是無效的盗冷,要么是受到了 App Store 的限制怠苔。但是如果你想要追蹤廣告的話,可以使用 IDFA仪糖,想要用來分析用戶行為可以通過 IDFV + KeyChain 的方式來解決柑司。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锅劝,隨后出現(xiàn)的幾起案子攒驰,更是在濱河造成了極大的恐慌,老刑警劉巖鸠天,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讼育,死亡現(xiàn)場離奇詭異,居然都是意外死亡稠集,警方通過查閱死者的電腦和手機奶段,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剥纷,“玉大人痹籍,你說我怎么就攤上這事』扌” “怎么了蹲缠?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長悠垛。 經(jīng)常有香客問我线定,道長,這世上最難降的妖魔是什么确买? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任斤讥,我火速辦了婚禮,結(jié)果婚禮上湾趾,老公的妹妹穿的比我還像新娘芭商。我一直安慰自己,他們只是感情好搀缠,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布铛楣。 她就那樣靜靜地躺著,像睡著了一般艺普。 火紅的嫁衣襯著肌膚如雪簸州。 梳的紋絲不亂的頭發(fā)上鉴竭,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機與錄音勿侯,去河邊找鬼拓瞪。 笑死,一個胖子當(dāng)著我的面吹牛助琐,可吹牛的內(nèi)容都是我干的祭埂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼兵钮,長吁一口氣:“原來是場噩夢啊……” “哼蛆橡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掘譬,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤泰演,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后葱轩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體睦焕,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年靴拱,在試婚紗的時候發(fā)現(xiàn)自己被綠了垃喊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡袜炕,死狀恐怖本谜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情偎窘,我是刑警寧澤乌助,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站陌知,受9級特大地震影響他托,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仆葡,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一上祈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浙芙,春花似錦、人聲如沸籽腕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皇耗。三九已至南窗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背万伤。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工窒悔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人敌买。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓简珠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容