最近写新项目,想搞一点高大上的,用keyChain来存储用户信息
keyChain的好处是可以使用苹果的加密来保证信息的安全,而且可以在app被删除之后保留信息,有传言在iOS10中keychain中的数据会随app一起删除,但是我用iOS11测试的结果是依然保留数据的
keyChain的另一个特性是同一个开发者帐号下的应用可以共享数据,这个我目前用不到
keyChain虽然有很多优点,但是读写信息还是挺麻烦的,需要两个string的认证,这相对于plist文件中的key-value相对来说麻烦一点
而且现在的应用一般不保留用户密码,而是用token来验证用户身份,所以目前keychain对我来说没有什么用
但是我还是要写一下keychain的使用方法哎嘿嘿
keychain是用SQLite进行存储的。用苹果的话来说是一个专业的数据库,加密我们保存的数据,可以通过metadata(attributes)进行高效的搜索。keychain适合保存一些比较小的数据量的数据,如果要保存大的数据,可以考虑文件的形式存储在磁盘上,在keychain里面保存解密这个文件的密钥。
keychain的类型
- kSecClassGenericPassword
- kSecClassInternetPassword
- kSecClassCertificate
- kSecClassKey
- kSecClassIdentity
不同类型对应的属性:
既然苹果是采用SQLite去存储的,那么以上这些不同item的attribute可以理解是数据库里面表的字段。那么对keychain的操作其实也就是普通数据库的增删改查了。这样也许就会觉得那些API也没那么难用了。
下面是我写的keychainmanager类
#import <Foundation/Foundation.h> @interface KeyChainManager : NSObject +(void)addInfoWith:(NSString *)info account:(NSString *)account service:(NSString *)service; +(void)deleteInfoWithAccount:(NSString *)account service:(NSString *)service; +(void)changeInfoWith:(NSString *)info account:(NSString *)account service:(NSString *)service; +(NSString *)getInfoWithAccount:(NSString *)account service:(NSString *)service; @end
#import "KeyChainManager.h" @implementation KeyChainManager
/*
向keychain中添加item
info是需要存储的信息
account,service是确认item的标识符,keychain通过这两个值来确定一个item,进行增删改查
*/
+(void)addInfoWith:(NSString *)info account:(NSString *)account service:(NSString *)service{ if (!info) { info = @""; } if (!account || [account isEqualToString:@""]) { NSLog(@"向keychain中添加item失败原因是kSecAttrAccount不存在"); return; } if (!service || [service isEqualToString:@""]) { NSLog(@"向keychain中添加item失败原因是kSecAttrService不存在"); return; } NSDictionary *query = @{(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecValueData : [info dataUsingEncoding:NSUTF8StringEncoding], (__bridge id)kSecAttrAccount :account, (__bridge id)kSecAttrService : service, }; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, nil); if (status == errSecSuccess) { NSLog(@"向keychain中添加item成功"); }else{ NSLog(@"向keychain中添加item失败%d",status); }; } //删除keychain中item +(void)deleteInfoWithAccount:(NSString *)account service:(NSString *)service{ if (!account || [account isEqualToString:@""]) { NSLog(@"删除keychain中item失败原因是kSecAttrAccount不存在"); return; } if (!service || [service isEqualToString:@""]) { NSLog(@"删除keychain中item失败原因是kSecAttrService不存在"); return; } NSDictionary *query = @{ (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrAccount :account, (__bridge id)kSecAttrService : service, }; OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); if (status == errSecSuccess) { NSLog(@"删除keychain中item成功"); }else{ NSLog(@"删除keychain中item失败%d",status); }; } //修改keychain中item数据 +(void)changeInfoWith:(NSString *)info account:(NSString *)account service:(NSString *)service{ if (!info) { info = @""; } if (!account || [account isEqualToString:@""]) { NSLog(@"修改keychain中item数据失败原因是kSecAttrAccount不存在"); return; } if (!service || [service isEqualToString:@""]) { NSLog(@"修改keychain中item数据失败原因是kSecAttrService不存在"); return; } NSDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrAccount :account, (__bridge id)kSecAttrService : service, }; NSDictionary *update = @{ (__bridge id)kSecValueData : [info dataUsingEncoding:NSUTF8StringEncoding], }; OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update); if (status == errSecSuccess) { NSLog(@"修改keychain中item数据成功"); }else{ NSLog(@"修改keychain中item数据失败%d",status); }; } //获取keychain中item数据 +(NSString *)getInfoWithAccount:(NSString *)account service:(NSString *)service{ if (!account || [account isEqualToString:@""]) { NSLog(@"获取keychain中item数据失败原因是kSecAttrAccount不存在"); return nil; } if (!service || [service isEqualToString:@""]) { NSLog(@"获取keychain中item数据失败原因是kSecAttrService不存在"); return nil; } NSDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecReturnData : @YES, (__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitOne, (__bridge id)kSecAttrAccount :account, (__bridge id)kSecAttrService : service, }; CFTypeRef dataTypeRef = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef); if (status == errSecSuccess) { NSString *pwd = [[NSString alloc] initWithData:(__bridge NSData * _Nonnull)(dataTypeRef) encoding:NSUTF8StringEncoding]; NSLog(@"获取keychain中item数据成功==result:%@", pwd); return pwd; }else{ NSLog(@"获取keychain中item数据失败%d",status); return nil; }; } @end
keychain可以进行应用间的数据共享
同一个开发者帐号下的应用可以共享存在keychain中的数据
这里需要到capabilities>keychain sharing
打开keychain sharing
可以看到有一个group,添加你想要获取数据的应用A的identifer,就可以获取它在keychain中的数据
可以对应用A的数据进行增删改查,但是这对应用A来说不是很安全