keychain(鑰匙串)存儲在iOS系統(tǒng)中,并且恢復iPhone會使keychain的內容也恢復.但是刪除App是不會影響keychain.
不同App之間Keychain是不能相互訪問的,但是可以通過配置keychain-sharing來解決。

如圖,在兩個項目中配置keychain-sharing,這樣就可以訪問互相的keychain,以此達到共享數(shù)據
如何使用
keychain是一個結構,有很多key-value



此外還有kSecReturnData(返回結果) kSecMatchLimit(查詢策略)等 這兩項是查詢時必選的
1.首選要構建一個存取條件,這些條件指定了要存取的這條內容的存取類型,唯一性,存取策略和內容
一共需要五個key
NSString *classKey = (__bridge NSString *)kSecClass;
//指定服務類型是普通密碼
NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
//指定安全類型是任何時候都可以訪問
NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
//指定服務的賬戶名 可以與服務名相同 賬戶名可以對應多個服務名
NSString *accountValue = accountId;
NSString *serviceKey = (__bridge NSString *)kSecAttrService;
//指定服務的名字 可以與服務賬戶名相同
NSString *serviceValue = serviceId;
//指定存儲的內容
NSString *valueDataKey = (__bridge NSString *)kSecValueData;
NSString *value = @"要存的內容";
NSDictionary *keychainItems = @{classKey : classValue,
accessibleKey : accessibleValue,
accountKey : accountValue,
serviceKey : serviceValue,
valueDataKey : value };
需要注意的是,賬戶名需要預防重名,因為keychain是系統(tǒng)級的,配置了keychain-sharing的其他App也可以訪問,一般使用類似bundleId的寫法
服務名稱就好像給宏取名字,表示存儲的這個東西是做什么的.
通過這兩個key就可以指定唯一性
kSecClassGenericPassword表示服務策略 key是kSecClass
kSecAttrAccessibleAlways是存取策略 key是kSecAttrAccessible
value是存儲的內容 key是kSecValueData
2.增刪改查
- (void)viewDidLoad {
[super viewDidLoad];
BOOL succeed = [self keychainSaveData:@"123456789" withAccountIdentifier:@"trigger" andServiceIdentifier:@"password"];
BOOL succeed1 = [self keychainSaveData:@"987654321" withAccountIdentifier:@"trigger" andServiceIdentifier:@"account"];
NSLog(@"%d",succeed);
NSLog(@"%d",succeed1);
NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"]);
NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"password"]);
[self keychainUpdataData:@"000000000" withAccountIdentifier:@"trigger" andServiceIdentifier:@"password"];
NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"password"]);
[self keychainDeleteWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"];
NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"]);
}
- (NSMutableDictionary *)keychainDicWithAccountId:(NSString *)accountId andServiceId:(NSString *)serviceId{
//構建一個存取條件,實質是一個字典
NSString *classKey = (__bridge NSString *)kSecClass;
//指定服務類型是普通密碼
NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
//指定安全類型是任何時候都可以訪問
NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
//指定服務的賬戶名 可以與服務名相同 賬戶名可以對應多個服務名
NSString *accountValue = accountId;
NSString *serviceKey = (__bridge NSString *)kSecAttrService;
//指定服務的名字 可以與服務賬戶名相同
NSString *serviceValue = serviceId;
NSDictionary *keychainItems = @{classKey : classValue,
accessibleKey : accessibleValue,
accountKey : accountValue,
serviceKey : serviceValue};
return keychainItems.mutableCopy;
}
- (BOOL)keychainSaveData:(id)aData withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
// 獲取存儲的數(shù)據的條件
NSMutableDictionary * saveQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 刪除舊的數(shù)據
SecItemDelete((CFDictionaryRef)saveQueryDic);
// 設置新的數(shù)據
[saveQueryDic setObject:[NSKeyedArchiver archivedDataWithRootObject:aData] forKey:(id)kSecValueData];
// 添加數(shù)據
OSStatus saveState = SecItemAdd((CFDictionaryRef)saveQueryDic, nil);
// 釋放對象
saveQueryDic = nil ;
// 判斷是否存儲成功
if (saveState == errSecSuccess) {
return YES;
}
return NO;
}
- (id)keychainGetDataWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
id idObject = nil ;
// 通過標記獲取數(shù)據查詢條件
NSMutableDictionary * readQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 查詢結果返回到 kSecValueData (此項必選)
[readQueryDic setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
// 只返回搜索到的第一條數(shù)據 (此項必選)
[readQueryDic setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
// 創(chuàng)建一個對象接受結果
CFDataRef keyChainData = nil ;
// 通過條件查詢數(shù)據
if (SecItemCopyMatching((CFDictionaryRef)readQueryDic , (CFTypeRef *)&keyChainData) == noErr){
@try {
//轉換類型
idObject = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)(keyChainData)];
} @catch (NSException * exception){
NSLog(@"Unarchive of search data where %@ failed of %@ ",serviceId,exception);
}
}
if (keyChainData) {
CFRelease(keyChainData);
}
readQueryDic = nil;
// 返回數(shù)據
return idObject ;
}
- (BOOL)keychainUpdataData:(id)data withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
// 通過標記獲取數(shù)據更新的條件
NSMutableDictionary * updataQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 創(chuàng)建更新數(shù)據字典
NSMutableDictionary * newDic = @{}.mutableCopy;
// 存儲數(shù)據
[newDic setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
// 獲取存儲的狀態(tài)
OSStatus updataStatus = SecItemUpdate((CFDictionaryRef)updataQueryDic, (CFDictionaryRef)newDic);
updataQueryDic = nil;
newDic = nil;
// 判斷是否更新成功
if (updataStatus == errSecSuccess) {
return YES ;
}
return NO;
}
- (void)keychainDeleteWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
// 獲取刪除數(shù)據的查詢條件
NSMutableDictionary * deleteQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 刪除指定條件的數(shù)據
SecItemDelete((CFDictionaryRef)deleteQueryDic);
deleteQueryDic = nil ;
}
作者:Trigger_o
鏈接:http://www.itdecent.cn/p/6c2265a82f72
來源:簡書
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。
](//upload-images.jianshu.io/upload_images/3238808-51b18f2d21779dd5.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200)
如圖,在兩個項目中配置keychain-sharing,這樣就可以訪問互相的keychain,以此達到共享數(shù)據
如何使用
keychain是一個結構,有很多key-value



此外還有kSecReturnData(返回結果) kSecMatchLimit(查詢策略)等 這兩項是查詢時必選的
1.首選要構建一個存取條件,這些條件指定了要存取的這條內容的存取類型,唯一性,存取策略和內容
一共需要五個key
NSString *classKey = (__bridge NSString *)kSecClass;
//指定服務類型是普通密碼
NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
//指定安全類型是任何時候都可以訪問
NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
//指定服務的賬戶名 可以與服務名相同 賬戶名可以對應多個服務名
NSString *accountValue = accountId;
NSString *serviceKey = (__bridge NSString *)kSecAttrService;
//指定服務的名字 可以與服務賬戶名相同
NSString *serviceValue = serviceId;
//指定存儲的內容
NSString *valueDataKey = (__bridge NSString *)kSecValueData;
NSString *value = @"要存的內容";
NSDictionary *keychainItems = @{classKey : classValue,
accessibleKey : accessibleValue,
accountKey : accountValue,
serviceKey : serviceValue,
valueDataKey : value };
需要注意的是,賬戶名需要預防重名,因為keychain是系統(tǒng)級的,配置了keychain-sharing的其他App也可以訪問,一般使用類似bundleId的寫法
服務名稱就好像給宏取名字,表示存儲的這個東西是做什么的.
通過這兩個key就可以指定唯一性
kSecClassGenericPassword表示服務策略 key是kSecClass
kSecAttrAccessibleAlways是存取策略 key是kSecAttrAccessible
value是存儲的內容 key是kSecValueData
2.增刪改查
- (void)viewDidLoad {
[super viewDidLoad];
BOOL succeed = [self keychainSaveData:@"123456789" withAccountIdentifier:@"trigger" andServiceIdentifier:@"password"];
BOOL succeed1 = [self keychainSaveData:@"987654321" withAccountIdentifier:@"trigger" andServiceIdentifier:@"account"];
NSLog(@"%d",succeed);
NSLog(@"%d",succeed1);
NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"]);
NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"password"]);
[self keychainUpdataData:@"000000000" withAccountIdentifier:@"trigger" andServiceIdentifier:@"password"];
NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"password"]);
[self keychainDeleteWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"];
NSLog(@"%@",(NSString *)[self keychainGetDataWithAccountIdentifier:@"trigger" andServiceIdentifier:@"account"]);
}
- (NSMutableDictionary *)keychainDicWithAccountId:(NSString *)accountId andServiceId:(NSString *)serviceId{
//構建一個存取條件,實質是一個字典
NSString *classKey = (__bridge NSString *)kSecClass;
//指定服務類型是普通密碼
NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
//指定安全類型是任何時候都可以訪問
NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
//指定服務的賬戶名 可以與服務名相同 賬戶名可以對應多個服務名
NSString *accountValue = accountId;
NSString *serviceKey = (__bridge NSString *)kSecAttrService;
//指定服務的名字 可以與服務賬戶名相同
NSString *serviceValue = serviceId;
NSDictionary *keychainItems = @{classKey : classValue,
accessibleKey : accessibleValue,
accountKey : accountValue,
serviceKey : serviceValue};
return keychainItems.mutableCopy;
}
- (BOOL)keychainSaveData:(id)aData withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
// 獲取存儲的數(shù)據的條件
NSMutableDictionary * saveQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 刪除舊的數(shù)據
SecItemDelete((CFDictionaryRef)saveQueryDic);
// 設置新的數(shù)據
[saveQueryDic setObject:[NSKeyedArchiver archivedDataWithRootObject:aData] forKey:(id)kSecValueData];
// 添加數(shù)據
OSStatus saveState = SecItemAdd((CFDictionaryRef)saveQueryDic, nil);
// 釋放對象
saveQueryDic = nil ;
// 判斷是否存儲成功
if (saveState == errSecSuccess) {
return YES;
}
return NO;
}
- (id)keychainGetDataWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
id idObject = nil ;
// 通過標記獲取數(shù)據查詢條件
NSMutableDictionary * readQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 查詢結果返回到 kSecValueData (此項必選)
[readQueryDic setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
// 只返回搜索到的第一條數(shù)據 (此項必選)
[readQueryDic setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
// 創(chuàng)建一個對象接受結果
CFDataRef keyChainData = nil ;
// 通過條件查詢數(shù)據
if (SecItemCopyMatching((CFDictionaryRef)readQueryDic , (CFTypeRef *)&keyChainData) == noErr){
@try {
//轉換類型
idObject = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)(keyChainData)];
} @catch (NSException * exception){
NSLog(@"Unarchive of search data where %@ failed of %@ ",serviceId,exception);
}
}
if (keyChainData) {
CFRelease(keyChainData);
}
readQueryDic = nil;
// 返回數(shù)據
return idObject ;
}
- (BOOL)keychainUpdataData:(id)data withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
// 通過標記獲取數(shù)據更新的條件
NSMutableDictionary * updataQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 創(chuàng)建更新數(shù)據字典
NSMutableDictionary * newDic = @{}.mutableCopy;
// 存儲數(shù)據
[newDic setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
// 獲取存儲的狀態(tài)
OSStatus updataStatus = SecItemUpdate((CFDictionaryRef)updataQueryDic, (CFDictionaryRef)newDic);
updataQueryDic = nil;
newDic = nil;
// 判斷是否更新成功
if (updataStatus == errSecSuccess) {
return YES ;
}
return NO;
}
- (void)keychainDeleteWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
// 獲取刪除數(shù)據的查詢條件
NSMutableDictionary * deleteQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 刪除指定條件的數(shù)據
SecItemDelete((CFDictionaryRef)deleteQueryDic);
deleteQueryDic = nil ;
}
注:文章出處
作者:Trigger_o
鏈接:http://www.itdecent.cn/p/6c2265a82f7