使用Keychain存储,解决应用卸载后仍有效

苹果提供Keychain存储方案在Security.framework,里面还有很多关于证书方面的内容,有兴趣的可以研究下。

屏幕快照 2016-11-04 下午5.31.53.png

其中Secitem.h供定义了四个方法:

OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
OSStatus SecItemUpdate(CFDictionaryRef query,
    CFDictionaryRef attributesToUpdate)
OSStatus SecItemDelete(CFDictionaryRef query)

相应的在封装时也需提供。下面直接贴代码:
GTRKeyChainStore.h

#import 

@interface GTRKeyChainStore : NSObject
@property (nonatomic, strong) NSString *uuid;
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, strong) NSString *pwd;

@end

GTRKeyChainStore.m

#import "GTRKeyChainStore.h"


@implementation GTRKeyChainStore

- (NSString *)prefixBundleID {
    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
    return bundleId ? bundleId:@"com.gtr.demo";
}

- (void)setUserName:(NSString *)userName {
    [self setKeyChainValue:userName forKey:[NSString stringWithFormat:@"%@username",[self prefixBundleID]]];
}

- (NSString *)userName {
    return [self getKeyChainValueForKey:[NSString stringWithFormat:@"%@username",[self prefixBundleID]]];
}

- (void)setPwd:(NSString *)pwd {
    [self setKeyChainValue:pwd forKey:[NSString stringWithFormat:@"%@pwd",[self prefixBundleID]]];
}

- (NSString *)pwd {
    return [self getKeyChainValueForKey:[NSString stringWithFormat:@"%@pwd",[self prefixBundleID]]];
}

- (void)setUuid:(NSString *)uuid {
    [self setKeyChainValue:uuid forKey:[NSString stringWithFormat:@"%@uuid",[self prefixBundleID]]];
}

- (NSString *)uuid {
    return [self getKeyChainValueForKey:[NSString stringWithFormat:@"%@uuid",[self prefixBundleID]]];
}

- (BOOL)setKeyChainValue:(NSString *)valueString forKey:(NSString *)keyString {
    if (!valueString) {
        [self deleteItemFromKeyChainWithIdentifier:keyString];
        return YES;
    }
    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    NSData *valueData = [valueString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *dict = @{
       (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
       (__bridge id)kSecAttrService: bundleId,
       (__bridge id)kSecAttrGeneric: keyData,
       (__bridge id)kSecAttrAccount: keyData,
       (__bridge id)kSecValueData: valueData,
       (__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
    };
    
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dict, NULL);
    if (errSecSuccess == status) {
        return YES;
    } else if (errSecDuplicateItem == status) {
        return [self updateKeyChainValue:valueString forKey:keyString];
    } else {
        return NO;
    }
}

- (BOOL)updateKeyChainValue:(NSString *)valueString forKey:(NSString *)keyString {
    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    NSData *valueData = [valueString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *dict = @{
       (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
       (__bridge id)kSecAttrService: bundleId,
       (__bridge id)kSecAttrGeneric: keyData,
       (__bridge id)kSecAttrAccount: keyData
    };
    NSDictionary *dataDict = @{
        (__bridge id)kSecValueData: valueData
    };
    
    OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)dict, (__bridge CFDictionaryRef)dataDict);
    if (errSecSuccess == status) {
        return YES;
    } else {
        return NO;
    }
}

- (NSString *)getKeyChainValueForKey:(NSString *)keyString {
    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *dict = @{
        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
        (__bridge id)kSecAttrService: bundleId,
        (__bridge id)kSecAttrGeneric: keyData,
        (__bridge id)kSecAttrAccount: keyData,
        (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne,
        (__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue
    };
    CFTypeRef found = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dict, &found);
    if (noErr == status) {
        NSData *result = (__bridge_transfer NSData *)found;
        return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
    } else {
        return nil;
    }
}

- (void)deleteItemFromKeyChainWithIdentifier:(NSString *)identifier {
    NSDictionary *searchDict = [self setupSearchDictionaryForIdentifier:identifier];;
    CFDictionaryRef dict = (__bridge CFDictionaryRef)searchDict;
    SecItemDelete(dict);
}

- (NSDictionary *)setupSearchDictionaryForIdentifier:(NSString *)identifier {
    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
    
    NSMutableDictionary *searchDict = [NSMutableDictionary dictionary];
    [searchDict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    [searchDict setObject:bundleId forKey:(__bridge id)kSecAttrService];
    
    NSData *keyData = [identifier dataUsingEncoding:NSUTF8StringEncoding];
    [searchDict setObject:keyData forKey:(__bridge id)kSecAttrGeneric];
    [searchDict setObject:keyData forKey:(__bridge id)kSecAttrAccount];
    return searchDict;
}

@end

测试代码

GTRKeyChainStore *store = [[GTRKeyChainStore alloc] init];
//    store.userName = @"gtr";
//    store.pwd = @"meiyoumima";
//    NSString *uuid = [NSString randomUUID];
//    if (uuid) {
//        store.uuid = uuid;
//    } else if ((uuid = [NSString appleIFA])) {
//        store.uuid = uuid;
//    } else {
//        store.uuid = [NSString appleIFV];
//    }
    NSLog(@"gtr username:%@",store.userName);
    NSLog(@"gtr username:%@",store.pwd);
    NSLog(@"gtr username:%@",store.uuid);

功能代码

+ (NSString *)appleIFA {
    NSString *ifa = nil;
    Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager");
    if (ASIdentifierManagerClass) {
        SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager");
        id sharedManager = ((id (*)(id, SEL))[ASIdentifierManagerClass methodForSelector:sharedManagerSelector])(ASIdentifierManagerClass, sharedManagerSelector);
        SEL advertisingIdentifierSelector = NSSelectorFromString(@"advertisingIdentifier");
        NSUUID *advertisingIdentifier = ((NSUUID* (*)(id, SEL))[sharedManager methodForSelector:advertisingIdentifierSelector])(sharedManager, advertisingIdentifierSelector);
        ifa = [advertisingIdentifier UUIDString];
    }
    return ifa;
}
+ (NSString *)appleIFV {
    if(NSClassFromString(@"UIDevice") && [UIDevice instancesRespondToSelector:@selector(identifierForVendor)]) {
        
        return [[UIDevice currentDevice].identifierForVendor UUIDString];
    }
    return nil;
}
+ (NSString *)randomUUID {
    if(NSClassFromString(@"NSUUID")) {
        return [[NSUUID UUID] UUIDString];
    }
    CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
    CFStringRef cfuuid = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
    CFRelease(uuidRef);
    NSString *uuid = [((__bridge NSString *) cfuuid) copy];
    CFRelease(cfuuid);
    return uuid;
}

直接复制黏贴可用。

你可能感兴趣的:(使用Keychain存储,解决应用卸载后仍有效)