持续化存储 --沙盒外 Keychain
我们使用NSUserDefaults存储数据信息,但是对于一些私密信息,但是对于一下比较私密的信息,如帐号、密码等等,我们就需要使用更为安全的keychain了。keychain保存的信息是保存在沙盒之外的,不会因App的删除而丢失,在用户重新安装了App后依然存在。其实可以把keychain理解成一个Dictionary,所有数据都以key-value
的形式存储,可以对这个Dictionary进行add、update、get、delete这四个操作。对一个应用来说,keychain都有两个访问区,私有和公共。
-
Target - Capabilities - Keychain Sharing - ON
图2.png左侧的目录会自动生成Entitlements文件,不需要自己创建了。
-
引入Security.framework
-
自定义一个类,取名keychain,如下:
.h
#import <Foundation/Foundation.h>
@interface keychain : NSObject
/**添加*/
+(void)savePassWord:(NSString *)password;
/**读取*/
+(id)loadPassWord;
/**删除*/
+(void)deletePassword;
@end
复制代码
.m文件
#import "keychain.h"
#import <Security/Security.h>
@implementation keychain
static NSString *const KEY_KEYCHAIN = @"LCF";
static NSString *const KEY_PASSWORD = @"PASSWORD";
/**添加*/
+(void)savePassWord:(NSString *)password{
NSMutableDictionary *infoDict = [NSMutableDictionary dictionary];
[infoDict setObject:password forKey:KEY_PASSWORD];
[keychain save:KEY_KEYCHAIN data:infoDict];
}
/**读取*/
+(id)loadPassWord{
NSMutableDictionary *infoDict = [keychain load:KEY_KEYCHAIN];
return [infoDict objectForKey:KEY_PASSWORD];
}
/**删除*/
+(void)deletePassword{
[self delete:KEY_KEYCHAIN];
}
+(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
网友评论