UDID替代方案--KeyChain

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

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

你可能感兴趣的:(UDID替代方案--KeyChain)