使用NSUserDefault是不安全的,因为是存储在沙盒中的,实际上是plist文件键值存储,并且最大的问题是存在与沙盒中,这就对安全性埋下了隐患。
KeyChain 服务存储,Keychain保存的数据不仅仅是加密过的,而且由于Keychain是存在与沙盒之外的,当应用删除之后,app 存储的数据并没有被删掉,第二次安装时只要读取Keychain里的数据,即可得到以前存储的信息。(比如配音秀,之前第一次安装的时候,我点击了不再显示***,然后变换了布局,后来卸载了之后再次安装,嗯,我还真的就再也不能看到之前的了,话说回来,这样真的符合用户交互么?你也不清楚用户想的啥啊~~)其实我觉得它这种做法,就好像手机链接WIFI 输入密码后,苹果电脑就可以直接连接不需要再输入密码了,虽然实现的方式不一样,但也的确是一种提供用户方便的一种措施。
Keychain使用场景:
存储隐私信息
iOS系统中,最常用的就是存储用户密码
数据共享
如果我们有多个 app,它们之间需要共享一些数据,以提供更好的用户体验,那么使用Keychain群组可以实现。但前提是同一个公司的产品才能共享,比如:com.hxyy.test1和com.hyyy.test2两个同一个公司下的不同产品之前可以实现数据共享。
设备唯一标示存储
在iOS中,为了在苹果的打压下获取唯一标示符,开发者们也是想尽了办法,目前最好的方式就是获取IDFV,并将其存储到Keychain中,IDFV是设备区别应用提供商的,一般来说可以作为应用唯一标示符,但是IDFV缺陷就是当设备删除了该所有应用提供商的app之后,IDFV值会发生变化,所以IDFV+Keychain的组合目前被经常用到,来替代UDID的作用,特别是加上Keychain的共享服务,可以使应用提供商下的所有app下获取的 IDFV都不会发生变化,这一服务可以说是目前最佳的识别用户的办法。
推荐的库:HYKeychainHelper库
下面我们来应用一下,如何使用Keychain
first 需要打开target - Capabilities - Keychain Sharing 点击打开为ON;
打开之后会自动创建Entitlement文件。也就是说,bundle Identifier、 Keychain Sharing 的Keychain Groups、 Entitlements 文件的Keychain Access Groups的第一个元素,
//
// KeychainStore.m
// KeychainDemo
//
// Created by vicki753-mac on 13/11/2017.
// Copyright © 2017 zhengyi. All rights reserved.
//
#import "KeychainStore.h"
@implementation KeychainStore
+(NSMutableDictionary *)getKeychainQuery:(NSString *)service{
return [NSMutableDictionary dictionaryWithObjectsAndKeys:(id)kSecClassGenericPassword, (id)kSecClass, service, (id)kSecAttrAccount, (id)kSecAttrAccessibleAfterFirstUnlock, (id)kSecAttrAccessible, nil];
}
+(void)save:(NSString *)service data:(id)data{
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery); // 删除之前的
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData]; //用键值对 归档存储
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);//添加现在的
}
+(id)load:(NSString *)service{
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
[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)deleteKeyData:(NSString *)service{
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
}
@end
网友评论