博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(绝对有用)iOS获取UUID,并使用keychain存储
阅读量:6887 次
发布时间:2019-06-27

本文共 7529 字,大约阅读时间需要 25 分钟。

原文链接

 

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的下载地址:

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)kSecClassGenericPasswordforKey:(id)kSecClass];

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

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

    

   return returnDictionary;

}

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

{

   NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];

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

         [returnDictionary setObject:(id)kSecClassGenericPasswordforKey:(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

转载于:https://www.cnblogs.com/hxwj/p/4828032.html

你可能感兴趣的文章
Android View 事件分发源码分析
查看>>
vue 2.0 - props
查看>>
RustCon Asia 实录 | Rust 在国内某视频网站的应用
查看>>
Vue遇上Analytics
查看>>
修改max_allowed_packet(允许执行的sql最大长度)
查看>>
node js 处理时间分析
查看>>
判断数据库、表和字段是否存在
查看>>
新手安装postgreSQL后无法连接服务器
查看>>
递归和动态规划
查看>>
java实现简单的控制台管理系统
查看>>
建造模式
查看>>
BZOJ 4025: 二分图
查看>>
openNebula rgister img instance vms error collections
查看>>
error Infos
查看>>
PL/sql配置相关
查看>>
[New Portal]Windows Azure Virtual Machine (3) 在VM上挂载磁盘
查看>>
字体随着ProgressBar的加载而滚动
查看>>
Handler 机制再了解
查看>>
如果你是前端工程师,把你的网站或者你知道的网站加进来吧
查看>>
阿里云产品头条(2017年12月刊)
查看>>