UDID替代方案--KeyChain

作者: 尹_路人 | 来源:发表于2016-04-19 09:44 被阅读505次

    author: yongqian
    date: 2014-11-13
    iOS5.0之后,苹果禁止开发者获取UDID了,但是没有什么事能难倒我们程序猿,我们总能找到各种方法达到唯一标识一个设备的方法,例如本文要讲的--使用keychain存储UUID替代UDID

    UDID

    UDID (即Unique Device Identifier)是一个由子母和数字组成的 40 个字符串的序号,用来区别包括 iPhones, iPads, 以及 iPod Touches等iOS设备,这些编码看起来是随机的,实际上是跟硬件设备特点相联系的。

    使用keyChain存储UUID替代UDID

    • 导入Security.framework

    • 创建工具类"JPKeyChain"

      JPKeychain.h
        //
        //  JPKeychain.h
        //  IFood517
        //
        //  Created by YYQ on 14/11/13.
        //  Copyright (c) 2014年 YYQ. All rights reserved.
        //
        
        #import <Foundation/Foundation.h>
        #import <Security/Security.h>
        
        @interface JPKeychain : NSObject
        + (void)save:(NSString *)service data:(id)data;
        + (id)load:(NSString *)service;
        + (void)delete:(NSString *)service;
        @end  
      
      JPKeychain.m
        //
        //  JPKeychain.m
        //  IFood517
        //
        //  Created by YYQ on 14/11/13.
        //  Copyright (c) 2014年 YYQ. All rights reserved.
        //
        
        #import "JPKeychain.h"
        
        @implementation JPKeychain
        + (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
            return [NSMutableDictionary dictionaryWithObjectsAndKeys:
                    (id)kSecClassGenericPassword,(id)kSecClass,
                    service, (id)kSecAttrService,
                    service, (id)kSecAttrAccount,
                    (id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
                    nil];
        }
        
        + (void)save:(NSString *)service data:(id)data {
            //Get search dictionary
            NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
            //Delete old item before add new item
            SecItemDelete((CFDictionaryRef)keychainQuery);
            //Add new object to search dictionary(Attention:the data format)
            [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
            //Add item to keychain with the search dictionary
            SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
        }
        
        + (id)load:(NSString *)service {
            id ret = nil;
            NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
            //Configure the search setting
            //Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
            [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 {
                    ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
                } @catch (NSException *e) {
                    NSLog(@"Unarchive of %@ failed: %@", service, e);
                } @finally {
                }
            }
            if (keyData)
                CFRelease(keyData);
            return ret;
        }
        
        + (void)delete:(NSString *)service {
            NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
            SecItemDelete((CFDictionaryRef)keychainQuery);
        }
        
        @end
      
    • 有了这个工具类,我们就可以在keyChain中存储一些关键数据,比如用户名,密码之类,那么我们同样也就可以把UUID存进去替代UDID了

    使用

    #import "CommonCrypto/CommonDigest.h"
    #import "JPKeychain.h"
    
    NSString * const KEY_UDID_INSTEAD = @"com.jpgk.app.udid.instead.test";
    
    
    #pragma mark - Keychain 获取UUID
    +(NSString *)getDeviceIDInKeychain
    {
        NSString *getUDIDInKeychain = (NSString *)[JPKeychain load:KEY_UDID_INSTEAD];
        NSLog(@"从keychain中获取到的 UDID_INSTEAD %@",getUDIDInKeychain);
        if (!getUDIDInKeychain ||[getUDIDInKeychain isEqualToString:@""]||[getUDIDInKeychain isKindOfClass:[NSNull class]]) {
            CFUUIDRef puuid = CFUUIDCreate( nil );
            CFStringRef uuidString = CFUUIDCreateString( nil, puuid );
            NSString * result = (NSString *)CFBridgingRelease(CFStringCreateCopy( NULL, uuidString));
            CFRelease(puuid);
            CFRelease(uuidString);
            [JPKeychain save:KEY_UDID_INSTEAD data:result];
            getUDIDInKeychain = (NSString *)[JPKeychain load:KEY_UDID_INSTEAD];
        }
        NSLog(@"最终 ———— UDID_INSTEAD %@",getUDIDInKeychain);
        return getUDIDInKeychain;
    }
    

    更新 ------------

    • 代码已上传到Git,并支持Pod(点击这里查看源码

    • Pod接入:

        pod 'KeyChain-UDID'
        pod install
      
    • 在需要使用的类中

        #import <YYQKeyChain.h>
        ...
        ...
        NSString *udid = [YYQKeyChain getUDIDWithUniqueKey:@"com.comname.app.udid.instead"];
        NSLog(@"udid from keyChain %@", udid);
      

    由此联想到是否可以保存用户名、密码之类的数据,用户即使卸载应用,下次安装后也可以提示用户是否允许从本地读取用户名密码用来登录,感兴趣的同学可以试一试。。。。。。

    相关文章

      网友评论

      • faa25893224a:[UIDevice currentDevice].identifierForVendor.UUIDString 这样获取的 和你用的那种方法有什么差别吗?
        faa25893224a:@文竹_bamboo 也就是一样的啊,[UIDevice currentDevice].identifierForVendor.UUIDString + keychain
        尹_路人:@好萌啊 [UIDevice currentDevice].identifierForVendor.UUIDString这里取得的是设备的UUID,UUID和UDID的区别Google一下就知道了,UUID在同一台设备上是可变的,例如卸载重装
        上文的方法本质上可以理解为:
        调用API生成一个UUID然后把这个UUID持久化到iOS设备,以达到代替UDID的目的

      本文标题:UDID替代方案--KeyChain

      本文链接:https://www.haomeiwen.com/subject/ctnalttx.html