美文网首页iOS程序猿iOS学习开发
使用Keychain存储,解决应用卸载后仍有效

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

作者: GTReload | 来源:发表于2016-11-04 17:41 被阅读0次

    苹果提供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 <Foundation/Foundation.h>
    
    @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存储,解决应用卸载后仍有效

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