目前需要保存一些用户配置,在app删掉后依然能正常读取,那么就用到【钥匙串】
项目中有时会需要存储敏感信息(如密码、密钥等),苹果官方提供了一种存储机制--钥匙串(keychain)。
keychain是一种存储在硬盘上的加密的数据库。这个可能是卸载App后,keychain信息还在的原因。
keychain适合存储 较小的数据量(不超过上千字节或上兆字节)的内容。
JJKeychain.h
@interface JJKeychain : NSObject
//保存是先删掉之前的key,没有使用update,感觉这样简单;然后保存的value转换为NSData,如果value为自定义object,则需遵循NSSecureCoding协议
+ (BOOL)setValue:(id)value forKey:(NSString \*)key;
+ (BOOL)setValue:(id)value forKey:(NSString \*)key forAccessGroup:(nullable NSString \*)group;
+ (id)valueForKey:(NSString \*)key;
+ (id)valueForKey:(NSString \*)key forAccessGroup:(nullable NSString \*)group;
+ (BOOL)deleteValueForKey:(NSString \*)key;
+ (BOOL)deleteValueForKey:(NSString \*)key forAccessGroup:(nullable NSString \*)group;
+ (NSString \*)getBundleSeedIdentifier;
@end
JJKeychain.m
@implementation JJKeychain
+ (NSMutableDictionary \*)getKeychainQuery:(NSString \*)key forAccessGroup:(NSString \*)group{
NSMutableDictionary \*query = @{(\_\_bridge id)kSecClass : (\_\_bridge id)kSecClassGenericPassword,
(\_\_bridge id)kSecAttrService : key,
(\_\_bridge id)kSecAttrAccount : key,
(\_\_bridge id)kSecAttrAccessible : (\_\_bridge id)kSecAttrAccessibleAfterFirstUnlock
}.mutableCopy;
if (group != nil) {
[query setObject:[self getFullAccessGroup:group] forKey:(\_\_bridge id)kSecAttrAccessGroup];
}
return query;
}
+ (NSString \*)getFullAccessGroup:(NSString \*)group
{
NSString \*accessGroup = nil;
NSString \*bundleSeedIdentifier = [self getBundleSeedIdentifier];
if (bundleSeedIdentifier != nil && [group rangeOfString:bundleSeedIdentifier].location == NSNotFound) {
accessGroup = [NSString stringWithFormat:@"%@.%@", bundleSeedIdentifier, group];
}
return accessGroup;
}
+ (NSString \*)getBundleSeedIdentifie
{
static \_\_strong NSString \*bundleSeedIdentifier = nil;
if (bundleSeedIdentifier == nil) {
@synchronized(self) {
if (bundleSeedIdentifier == nil) {
NSString \*\_bundleSeedIdentifier = nil;
NSDictionary \*query = @{
(\_\_bridge id)kSecClass: (\_\_bridge NSString \*)kSecClassGenericPassword,
(\_\_bridge id)kSecAttrAccount: @"bundleSeedID",
(\_\_bridge id)kSecAttrService: @"",
(\_\_bridge id)kSecReturnAttributes: (\_\_bridge id)kCFBooleanTrue
};
CFDictionaryRef result = nil;
OSStatus status = SecItemCopyMatching((\_\_bridge CFDictionaryRef)query, (CFTypeRef \*)&result);
if (status == errSecItemNotFound) {
status = SecItemAdd((\_\_bridge CFDictionaryRef)query, (CFTypeRef \*)&result);
}
if (status == errSecSuccess) {
NSString \*accessGroup = [(\_\_bridge NSDictionary \*)result objectForKey:(\_\_bridge NSString \*)kSecAttrAccessGroup];
NSArray \*components = [accessGroup componentsSeparatedByString:@"."];
// NSLog(@"components %@",components);
\_bundleSeedIdentifier = [[components objectEnumerator] nextObject];
CFRelease(result);
}
if (\_bundleSeedIdentifier != nil) {
bundleSeedIdentifier = [\_bundleSeedIdentifier copy];
}
}
}
}
return bundleSeedIdentifier;
}
+ (BOOL)setValue:(id)value forKey:(NSString \*)key{
return [self setValue:value forKey:key forAccessGroup:nil];
}
+ (BOOL)setValue:(id)value forKey:(NSString \*)key forAccessGroup:(NSString \*)group{
NSMutableDictionary \*query = [self getKeychainQuery:key forAccessGroup:group];
[self deleteValueForKey:key forAccessGroup:group];
NSData \*data = nil;
@try {
data = [NSKeyedArchiver archivedDataWithRootObject:value];
} @catch (NSException \*exception) {
NSLog(@"archived failure value %@ %@",value,exception);
return NO;
}
[query setObject:data forKey:(\_\_bridge id)kSecValueData];
OSStatus result = SecItemAdd((\_\_bridge CFDictionaryRef)query, NULL);
return result == errSecSuccess;
}
+ (BOOL)deleteValueForKey:(NSString \*)key{
return [self deleteValueForKey:key forAccessGroup:nil];
}
+ (BOOL)deleteValueForKey:(NSString \*)key forAccessGroup:(NSString \*)group{
NSMutableDictionary \*query = [self getKeychainQuery:key forAccessGroup:group];
OSStatus result = SecItemDelete((\_\_bridge CFDictionaryRef)query);
return result == errSecSuccess;
}
+ (id)valueForKey:(NSString \*)key{
return [self valueForKey:key forAccessGroup:nil];
}
+ (id)valueForKey:(NSString \*)key forAccessGroup:(NSString \*)group{
id value = nil;
NSMutableDictionary \*query = [self getKeychainQuery:key forAccessGroup:group];
CFDataRef keyData = NULL;
[query setObject:(\_\_bridge id)kCFBooleanTrue forKey:(\_\_bridge id)kSecReturnData];
[query setObject:(\_\_bridge id)kSecMatchLimitOne forKey:(\_\_bridge id)kSecMatchLimit];
if (SecItemCopyMatching((\_\_bridge CFDictionaryRef)query, (CFTypeRef \*)&keyData) == errSecSuccess) {
@try {
value = [NSKeyedUnarchiver unarchiveObjectWithData:(\_\_bridge NSData \*)keyData];
}
@catch (NSException \*e) {
NSLog(@"Unarchive of %@ failed: %@", key, e);
value = nil;
}
}
if (keyData) {
CFRelease(keyData);
}
return value;
}
@end
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。