zoukankan      html  css  js  c++  java
  • (绝对有用)iOS获取UUID,并使用keychain存储

    原文链接 http://blog.sina.com.cn/s/blog_5971cdd00102vqgy.html

     

    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 = [NSMutableDictionary dictionaryWithDictionary: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. ");

        }

        

        [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文件。

             (绝对有用)iOS获取UUID,并使用keychain存储


    该文件的结构中最顶层的节点必须是一个名为“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,如下图。
              (绝对有用)iOS获取UUID,并使用keychain存储

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

      NSString * uuid= [UUID getUUID];

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

      测试结果:
      uuid=19AAB430-9CB8-4325-ACC5-D7D386B68960

  • 相关阅读:
    解决IE新开窗口无法显示内容的问题
    日历控件
    关于索引,我们可以知道的更多 全表扫描和索引扫描
    C#构造函数的小说明
    动态创建的组件的ID设定
    正则表达式使用详解
    “运行”命令全集
    数组型参数
    接口实现的继承机制
    读取XML文件
  • 原文地址:https://www.cnblogs.com/hxwj/p/4828032.html
Copyright © 2011-2022 走看看