iOS的UDID廢用以及UUID配合keychain的替換方案實現
首先,簡單介紹一下UDID這個東西:
UDID是Unique Device Identifier的簡稱,也就是唯一裝置標識的意思。於iOS SDK中取得的方法是UIDevice的一個叫uniqueIdentifier的NSString*,由於這個ID字串是基於裝置的,應用開發人員可以通過獲取此ID來用於記錄區分裝置。正是由於這個特性,可能會導致一些隱私等等相關的問題,Apple於iOS5中將這個UDID廢掉了,SDK中被標記為了Deprecated,雖然為了相容低版本的原始碼而繼續存在,但並不會再返回任何有實際意義的東西。
最近在做Flurry的統計功能時,發現還是需要用到可以識別裝置的東西的,好方便分析資料,經過一段時間的研究、試驗,發現了這個應該還算是比較靠譜的方法……
其實早在UDID被deprecated的訊息剛出來時,就已經有很多人開始研究對策了,我也google到了各種五花八門的解決方案,最後還是覺得這個UUID的方案比較合適,畢竟是蘋果官方文件裡推薦的替換UDID的方法。
關於UUID的具體說明可以檢視下面參考文章中給出的蘋果官方文件連結。簡單來說,UUID就是一個隨機序列字串生成器,有點像Microsoft Windows的COM GUID生成器的作用,比起自己隨機一個字串的好處就是這東西能夠保證唯一性,適用於標記。呼叫方法如下:
1 |
CFUUIDRef
uuidRef = CFUUIDCreate(kCFAllocatorDefault); |
2 |
NSString
* uuid =
(NSString *)CFUUIDCreateString (kCFAllocatorDefault,uuidRef); |
然後呢,官方建議的做法是用:
1 |
NSUserDefaults *userDefaults
= [ NSUserDefaults standardUserDefaults]; |
2 |
[userDefaults
setObject: uuid forKey:@"UUID"]; |
這樣的做法把生成的ID儲存起來,下次再用的時候就直接讀取已經儲存的ID了。顯然,那個UUID生成只是個“隨機字串”生成器,並不能像UDID那樣保證每次取得的串都一樣!儲存起來雖然能保證使用者再次開啟這個應用時,能夠獲得一致的標識ID,但不能保證使用者刪除應用重新安裝後這個ID的一致性,因為NSUserDefaults只是個像遊戲存檔一樣的東西,遊戲刪了,存檔也就跟著一塊刪了。所以,這個“存存檔”的方法並不是一個比較完善的解決方案,一個更好的做法是利用keychain儲存這個生成的UUID。
關於keychain這個東西的概念可以到這裡學習:https://developer.apple.com/library/ios/#documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html,簡言之就是每個應用程式都有一個可以用於安全儲存一些如密碼、認證等資訊的keychain,通過對應用簽名時的一些設定,還可以利用keychain的方式實現同一開發者簽證(就是相同bundle seed)下的不同應用之間共享資訊的操作。比如你有一個開發者帳戶,並開發了兩個不同的應用A和B,然後通過對A和B的keychain access group這個東西指定共用的訪問分組,就可以實現共享此keychain中的內容。而且,對比NSUserDefaults的一點不同之處就是此資訊不會隨應用的刪除而消失!
關於Keychain的應用,Apple提供了一個叫GenericKeychain的例子程式,在這裡:http://developer.apple.com/library/ios/#samplecode/GenericKeychain/Listings/Classes_KeychainItemWrapper_h.html#//apple_ref/doc/uid/DTS40007797-Classes_KeychainItemWrapper_h-DontLinkElementID_9,其中封裝了一個簡化Keychain操作的類:KeychainItemWrapper,可以拿來直接使用,記得加入Security.framework!
參考程式碼如下:
1 |
KeychainItemWrapper
*keychainItem = [[KeychainItemWrapper alloc] |
2 |
initWithIdentifier: @"UUID" |
3 |
accessGroup: @"YOUR_BUNDLE_SEED.com.yourcompany.userinfo" ]; |
4 |
NSString *strUUID
= [keychainItem objectForKey:( id )kSecValueData]; |
5 |
if ([strUUID
isEqualToString: @"" ]) |
6 |
{ |
7 |
CFUUIDRef
uuidRef = CFUUIDCreate(kCFAllocatorDefault); |
8 |
strUUID
= ( NSString *)CFUUIDCreateString
(kCFAllocatorDefault,uuidRef); |
9 |
[keychainItem
setObject:strUUID forKey:( id )kSecValueData]; |
10 |
} |
11 |
[FlurryAnalytics
setUserID:strUUID]; |
12 |
[keychainItem
release]; |
關於keychain access groups的設定,傳統方法是在Xcode專案target的Build Settings的Code Signing段中加入Code Signing Entitlements的配置檔案,加入group資訊,詳細操作搜尋一下就能找到。新版本的Xcode直接整合了生成Entitlements的功能,在指定target的Summary的最後一個段Entitlements中勾選Enable Entitlements,然後在下面的Keychain Access Groups中加入”com.yourcompany.userinfo”類的共享group名。這裡要注意一點,參考IDE預設生成的對應app自身id名的group可以發現,這裡的group並沒有加入bundle
seed,看一下生成的entitlements檔案中的內容可以發現group名頭部被自動新增了“$(AppIdentifierPrefix)”這種替換變數!
在最開始測試Keychain讀寫時,參考下面”如何解決蘋果公司禁止用UUID的方法”這篇文章中的方法,一直在keychainItem setObject時報異常,斷點跟蹤了一下,發現是封裝類的writeToKeychain函式中的SecItemAdd函式返回
errSecParam = -50, /* One or more parameters passed to a function where not valid. */
這個錯誤,最初以為是access group設定的問題,替換引數為nil後報錯依舊.最後google到stack overflow上的”Storing keys in KeyChain with KeyChainItemWrapper”這篇問答後意識到原來setObject的key不能隨意指定任意的string,而必須使用預定義好的一些key,替換key為kSecValueData後,問題解決。
參考文章:
Deprecated UIDevice Methodshttps://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIDevice_Class/DeprecationAppendix/AppendixADeprecatedAPI.html#//apple_ref/occ/instp/UIDevice/uniqueIdentifier
如何解決蘋果公司禁止用UUID的方法。 http://blog.csdn.net/mengtnt/article/details/7410373
Storing keys in KeyChain with KeyChainItemWrapper http://stackoverflow.com/questions/7117885/storing-keys-in-keychain-with-keychainitemwrapper
相關文章
- iOS UDID與UUIDiOSUI
- UDID和UUID的區別UI
- [分享]iOS開發 - iOS自動佈局的替換方案iOS
- 開發中常用工具 - 獲取裝置的唯一標識、UDID、UUID、keychain儲存UUID、判斷網路型別等UIAI型別
- 基於多重替換方式的iOS程式碼混淆方案iOS
- 【iOS】使用UUID+KeyChain記錄裝置唯一標識iOSUIAI
- 實現最簡單的模板替換
- Thunk程式的實現原理以及在iOS中的應用iOS
- Swift iOS : KeyChainSwiftiOSAI
- iOS KeyChain 應用間共享資料iOSAI
- Thunk程式的實現原理以及在iOS中的應用(二)iOS
- iOS KeyChain儲存iOSAI
- jQuery實現的將指定元素中的內容替換jQuery
- js實現的替換字串中的全部指定內容JS字串
- 如何獲取iOS裝置的UDIDiOS
- canvas實現動態替換人物的背景顏色Canvas
- WPS中實現文件特定字元的字型替換字元
- Nginx實現對響應體內容的替換Nginx
- python 字串替換可以用2種方法實現:Python字串
- 配合Masonry實現TableViewCell的高度自適應,以及更易管理的高度快取View快取
- [iOS]This will result in loss of keychain access ?iOSAI
- 用閉包替換遞迴實現斐波拉契數列遞迴
- Openstack的HA解決方案【替換原有的dashboard】
- Microsoft.AspNet.Web.Optimization.Bundle的完美替換方案ROSWeb
- [OOD]違反里氏替換原則的解決方案
- js實現的對字串中的指定內容進行替換操作JS字串
- 用javascript替換URL中的引數值JavaScript
- vim的批量替換
- nginx使用replace-filter-nginx-module實現內容替換的示例NginxFilter
- WinForm使用DataGridView實現類似Excel表格的查詢替換ORMViewExcel
- Android:使用SpannableString實現圖片替換相應的文字Android
- iOS安全攻防(九):使用Keychain-Dumper匯出keychain資料iOSAI
- LRU快取替換策略及C#實現快取C#
- 萌新如何用Python實現人臉替換?Python
- javascript實現拖拽並替換網頁塊元素JavaScript網頁
- 替換空格 將一個字串中的空格替換成“ ”字串
- handlebars.js 用 <br>替換掉 內容的換行符JS
- gohook 一個支援執行時替換 golang 函式的庫實現HookGolang函式