您的位置:首页 > 产品设计 > UI/UE

iOS获取UUID,并使用keychain存储

2015-07-21 17:57 603 查看
UDID被弃用,使用UUID来作为设备的唯一标识。获取到UUID后,如果用NSUserDefaults存储,当程序被卸载后重装时,再获得的UUID和之前就不同了。使用keychain存储可以保证程序卸载重装时,UUID不变。但当刷机或者升级系统后,UUID还是会改变的。但这仍是目前为止最佳的解决办法了,如果有更好的解决办法,欢迎留言。

(我整理的解决办法的参考来源:http://blog.k-res.net/archives/1081.html)

给大家两个类:

UUID.h中的代码:

#import 尖括号(Foundation/Foundation.h)

@interface UUID : NSObject

+(NSString *)getUUID;

@end

UUID.m中的代码:

#import "UUID.h"

#import 尖括号(Foundation/Foundation.h)

#import "KeychainItemWrapper.h"

@implementation UUID

+(NSString *)getUUID

{

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]

initWithIdentifier:@"UUID"

accessGroup:@"YOUR_BUNDLE_SEED.com.yourcompany.userinfo"];

NSString *strUUID = [keychainItem objectForKey:(id)CFBridgingRelease(kSecValueData)];

//首次执行该方法时,uuid为空

if ([strUUID isEqualToString:@""])

{

CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);

strUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault,uuidRef));

[keychainItem setObject:strUUID forKey:(id)CFBridgingRelease(kSecValueData)];

}

return strUUID;

}

@end

第二个类是苹果官方的一个Demo里封装的,Demo的下载地址:http://developer.apple.com/library/ios/#samplecode/GenericKeychain/Listings/Classes_KeychainItemWrapper_h.html#//apple_ref/doc/uid/DTS40007797-Classes_KeychainItemWrapper_h-DontLinkElementID_9

KeychainItemWrapper.h中的代码:

#import 尖括号(UIKit/UIKit.h)

@interface KeychainItemWrapper : NSObject

{

NSMutableDictionary *keychainItemData;

NSMutableDictionary *genericPasswordQuery;

}

@property (nonatomic, retain) NSMutableDictionary *keychainItemData;

@property (nonatomic, retain) NSMutableDictionary *genericPasswordQuery;

- (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *)
accessGroup;

- (void)setObject:(id)inObject forKey:(id)key;

- (id)objectForKey:(id)key;

- (void)resetKeychainItem;

@end

KeychainItemWrapper.m中的代码:

#import "KeychainItemWrapper.h"

#import 尖括号(Security/Security.h)

@interface KeychainItemWrapper (PrivateMethods)

- (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert;

- (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert;

- (void)writeToKeychain;

@end

@implementation KeychainItemWrapper

@synthesize keychainItemData, genericPasswordQuery;

- (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *)
accessGroup;

{

if (self = [super init])

{

genericPasswordQuery = [[NSMutableDictionary alloc] init];

[genericPasswordQuery setObject:(id)CFBridgingRelease(kSecClassGenericPassword) forKey:(id)kSecClass];

[genericPasswordQuery setObject:identifier forKey:(id)CFBridgingRelease(kSecAttrGeneric)];

if (accessGroup != nil)

{

#if TARGET_IPHONE_SIMULATOR

#else

[genericPasswordQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup];

#endif

}

[genericPasswordQuery setObject:(id)CFBridgingRelease(kSecMatchLimitOne) forKey:(id)kSecMatchLimit];

[genericPasswordQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];

NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];

NSMutableDictionary *outDictionary = nil;

if (! SecItemCopyMatching((CFDictionaryRef)tempQuery,
(CFTypeRef*)&outDictionary) == noErr)

{

[self resetKeychainItem];

[keychainItemData setObject:identifier forKey:(id)kSecAttrGeneric];

if (accessGroup != nil)

{

#if TARGET_IPHONE_SIMULATOR

#else

[keychainItemData setObject:accessGroup forKey:(id)kSecAttrAccessGroup];

#endif

}

}

else

{

self.keychainItemData =
[self secItemFormatToDictionary:outDictionary];

}

[outDictionary release];

}

return self;

}

- (void)dealloc

{

[keychainItemData release];

[genericPasswordQuery release];

[super dealloc];

}

- (void)setObject:(id)inObject forKey:(id)key

{

if (inObject == nil) return;

id currentObject = [keychainItemData objectForKey:key];

if (![currentObject isEqual:inObject])

{

[keychainItemData setObject:inObject forKey:key];

[self writeToKeychain];

}

}

- (id)objectForKey:(id)key

{

return [keychainItemData objectForKey:key];

}

- (void)resetKeychainItem

{

OSStatus junk = noErr;

if (!keychainItemData)

{

self.keychainItemData = [[NSMutableDictionary alloc] init];

}

else if (keychainItemData)

{

NSMutableDictionary *tempDictionary = [self dictionaryToSecItemFormat:keychainItemData];

junk = SecItemDelete((CFDictionaryRef)tempDictionary);

NSAssert(
junk == noErr || junk == errSecItemNotFound, @"Problem
deleting current dictionary." );

}

[keychainItemData setObject:@"" forKey:(id)kSecAttrAccount];

[keychainItemData setObject:@"" forKey:(id)kSecAttrLabel];

[keychainItemData setObject:@"" forKey:(id)kSecAttrDescription];

[keychainItemData setObject:@"" forKey:(id)kSecValueData];

}

- (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert

{

NSMutableDictionary *returnDictionary
= [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

[returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

NSString *passwordString = [dictionaryToConvert objectForKey:(id)kSecValueData];

[returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding]forKey:(id)kSecValueData];

return returnDictionary;

}

- (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert

{

NSMutableDictionary *returnDictionary = [NSMutableDictionarydictionaryWithDictionary:dictionaryToConvert];

[returnDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];

[returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

NSData *passwordData = NULL;

if (SecItemCopyMatching((CFDictionaryRef)returnDictionary,
(CFTypeRef*)&passwordData) == noErr)

{

[returnDictionary removeObjectForKey:(id)kSecReturnData];

NSString *password
= [[[NSString alloc] initWithBytes:[passwordData bytes]length:[passwordData length]

encoding:NSUTF8StringEncoding]autorelease];

[returnDictionary setObject:password forKey:(id)kSecValueData];

}

else

{

NSAssert(NO, @"Serious
error, no matching item found in the keychain.\n");

}

[passwordData release];

return returnDictionary;

}

- (void)writeToKeychain

{

NSDictionary *attributes = NULL;

NSMutableDictionary *updateItem = NULL;

OSStatus result;

if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery,
(CFTypeRef*)&attributes) == noErr)

{

updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes];

[updateItem setObject:[genericPasswordQuery objectForKey:(id)kSecClass] forKey:(id)kSecClass];

NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData];

[tempCheck removeObjectForKey:(id)kSecClass];

#if TARGET_IPHONE_SIMULATOR

[tempCheck removeObjectForKey:(id)kSecAttrAccessGroup];

#endif

result = SecItemUpdate((CFDictionaryRef)updateItem,
(CFDictionaryRef)tempCheck);

NSAssert( result == noErr, @"Couldn't
update the Keychain Item." );

}

else

{

result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);

NSAssert( result == noErr, @"Couldn't
add the Keychain Item." );

}

}

@end

PS: 1. KeychainItemWrapper类在官方Demo里面也有,如果我复制出错了,大家可以在上面的链接上下载官方Demo.
2.使用时要添加Security.framework
3. 尤为注意的是,UUID类下面这句代码中group的设置方法。
KeychainItemWrapper *keychainItem
= [[KeychainItemWrapper alloc]

initWithIdentifier:@"UUID"

accessGroup:@"YOUR_BUNDLE_SEED.com.yourcompany.userinfo"];

(1)在项目相同的目录下创建KeychainAccessGroups.plist文件。





该文件的结构中最顶层的节点必须是一个名为“keychain-access-groups”的Array,并且该Array中每一项都是一个描述分组的NSString。YOUR_BUNDLE_SEED.com.yourcompany.userinfo就是要设置的组名。
(2)在项目相同的目录下创建KeychainAccessGroups.plist文件。在Target-Build
Settings-Code Signing栏下的Code Signing Entitlements右侧添加KeychainAccessGroups.plist,如下图。




到此,工作就完成了。
首次安装程序时,打印出一个uuid,当把程序卸载后,再用getUUID获得
uuid,打印出来的结果和之前相同。证明达到目的。

测试代码:

NSString * uuid= [UUID getUUID];

NSLog(@"uuid=%@",uuid);

测试结果:
uuid=19AAB430-9CB8-4325-ACC5-D7D386B68960
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: