keychain(鑰匙串)存儲(chǔ)在iOS系統(tǒng)中,并且恢復(fù)iPhone會(huì)使keychain的內(nèi)容也恢復(fù).但是刪除App是不會(huì)影響keychain.
不同App之間Keychain是不能相互訪問的,但是可以通過配置keychain-sharing來解決。
![keychain(鑰匙串)存儲(chǔ)在iOS系統(tǒng)中,并且恢復(fù)iPhone會(huì)使keychain的內(nèi)容也恢復(fù).但是刪除App是不會(huì)影響keychain.
不同App之間Keychain是不能相互訪問的招盲,但是可以通過配置keychain-sharing來解決制市。
如圖,在兩個(gè)項(xiàng)目中配置keychain-sharing,這樣就可以訪問互相的keychain,以此達(dá)到共享數(shù)據(jù)
如何使用
keychain是一個(gè)結(jié)構(gòu),有很多key-value
此外還有kSecReturnData(返回結(jié)果) kSecMatchLimit(查詢策略)等 這兩項(xiàng)是查詢時(shí)必選的
1.首選要構(gòu)建一個(gè)存取條件,這些條件指定了要存取的這條內(nèi)容的存取類型,唯一性,存取策略和內(nèi)容
一共需要五個(gè)key
NSString *classKey = (__bridge NSString *)kSecClass;
//指定服務(wù)類型是普通密碼
NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
//指定安全類型是任何時(shí)候都可以訪問
NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
//指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對(duì)應(yīng)多個(gè)服務(wù)名
NSString *accountValue = accountId;
NSString *serviceKey = (__bridge NSString *)kSecAttrService;
//指定服務(wù)的名字 可以與服務(wù)賬戶名相同
NSString *serviceValue = serviceId;
//指定存儲(chǔ)的內(nèi)容
NSString *valueDataKey = (__bridge NSString *)kSecValueData;
NSString *value = @"要存的內(nèi)容";
NSDictionary *keychainItems = @{classKey : classValue,
accessibleKey : accessibleValue,
accountKey : accountValue,
serviceKey : serviceValue,
valueDataKey : value };
需要注意的是,賬戶名需要預(yù)防重名,因?yàn)閗eychain是系統(tǒng)級(jí)的,配置了keychain-sharing的其他App也可以訪問,一般使用類似bundleId的寫法
服務(wù)名稱就好像給宏取名字,表示存儲(chǔ)的這個(gè)東西是做什么的.
通過這兩個(gè)key就可以指定唯一性
kSecClassGenericPassword表示服務(wù)策略 key是kSecClass
kSecAttrAccessibleAlways是存取策略 key是kSecAttrAccessible
value是存儲(chǔ)的內(nèi)容 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{
//構(gòu)建一個(gè)存取條件,實(shí)質(zhì)是一個(gè)字典
NSString *classKey = (__bridge NSString *)kSecClass;
//指定服務(wù)類型是普通密碼
NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
//指定安全類型是任何時(shí)候都可以訪問
NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
//指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對(duì)應(yīng)多個(gè)服務(wù)名
NSString *accountValue = accountId;
NSString *serviceKey = (__bridge NSString *)kSecAttrService;
//指定服務(wù)的名字 可以與服務(wù)賬戶名相同
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{
// 獲取存儲(chǔ)的數(shù)據(jù)的條件
NSMutableDictionary * saveQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 刪除舊的數(shù)據(jù)
SecItemDelete((CFDictionaryRef)saveQueryDic);
// 設(shè)置新的數(shù)據(jù)
[saveQueryDic setObject:[NSKeyedArchiver archivedDataWithRootObject:aData] forKey:(id)kSecValueData];
// 添加數(shù)據(jù)
OSStatus saveState = SecItemAdd((CFDictionaryRef)saveQueryDic, nil);
// 釋放對(duì)象
saveQueryDic = nil ;
// 判斷是否存儲(chǔ)成功
if (saveState == errSecSuccess) {
return YES;
}
return NO;
}
- (id)keychainGetDataWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
id idObject = nil ;
// 通過標(biāo)記獲取數(shù)據(jù)查詢條件
NSMutableDictionary * readQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 查詢結(jié)果返回到 kSecValueData (此項(xiàng)必選)
[readQueryDic setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
// 只返回搜索到的第一條數(shù)據(jù) (此項(xiàng)必選)
[readQueryDic setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
// 創(chuàng)建一個(gè)對(duì)象接受結(jié)果
CFDataRef keyChainData = nil ;
// 通過條件查詢數(shù)據(jù)
if (SecItemCopyMatching((CFDictionaryRef)readQueryDic , (CFTypeRef *)&keyChainData) == noErr){
@try {
//轉(zhuǎn)換類型
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;
// 返回?cái)?shù)據(jù)
return idObject ;
}
- (BOOL)keychainUpdataData:(id)data withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
// 通過標(biāo)記獲取數(shù)據(jù)更新的條件
NSMutableDictionary * updataQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 創(chuàng)建更新數(shù)據(jù)字典
NSMutableDictionary * newDic = @{}.mutableCopy;
// 存儲(chǔ)數(shù)據(jù)
[newDic setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
// 獲取存儲(chǔ)的狀態(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ù)據(jù)的查詢條件
NSMutableDictionary * deleteQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 刪除指定條件的數(shù)據(jù)
SecItemDelete((CFDictionaryRef)deleteQueryDic);
deleteQueryDic = nil ;
}
作者:Trigger_o
鏈接:http://www.reibang.com/p/6c2265a82f72
來源:簡(jiǎn)書
著作權(quán)歸作者所有无切。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)湖笨,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處黄刚。
](//upload-images.jianshu.io/upload_images/3238808-51b18f2d21779dd5.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200)
如圖,在兩個(gè)項(xiàng)目中配置keychain-sharing,這樣就可以訪問互相的keychain,以此達(dá)到共享數(shù)據(jù)
如何使用
keychain是一個(gè)結(jié)構(gòu),有很多key-value
此外還有kSecReturnData(返回結(jié)果) kSecMatchLimit(查詢策略)等 這兩項(xiàng)是查詢時(shí)必選的
1.首選要構(gòu)建一個(gè)存取條件,這些條件指定了要存取的這條內(nèi)容的存取類型,唯一性,存取策略和內(nèi)容
一共需要五個(gè)key
NSString *classKey = (__bridge NSString *)kSecClass;
//指定服務(wù)類型是普通密碼
NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
//指定安全類型是任何時(shí)候都可以訪問
NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
//指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對(duì)應(yīng)多個(gè)服務(wù)名
NSString *accountValue = accountId;
NSString *serviceKey = (__bridge NSString *)kSecAttrService;
//指定服務(wù)的名字 可以與服務(wù)賬戶名相同
NSString *serviceValue = serviceId;
//指定存儲(chǔ)的內(nèi)容
NSString *valueDataKey = (__bridge NSString *)kSecValueData;
NSString *value = @"要存的內(nèi)容";
NSDictionary *keychainItems = @{classKey : classValue,
accessibleKey : accessibleValue,
accountKey : accountValue,
serviceKey : serviceValue,
valueDataKey : value };
需要注意的是,賬戶名需要預(yù)防重名,因?yàn)閗eychain是系統(tǒng)級(jí)的,配置了keychain-sharing的其他App也可以訪問,一般使用類似bundleId的寫法
服務(wù)名稱就好像給宏取名字,表示存儲(chǔ)的這個(gè)東西是做什么的.
通過這兩個(gè)key就可以指定唯一性
kSecClassGenericPassword表示服務(wù)策略 key是kSecClass
kSecAttrAccessibleAlways是存取策略 key是kSecAttrAccessible
value是存儲(chǔ)的內(nèi)容 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{
//構(gòu)建一個(gè)存取條件,實(shí)質(zhì)是一個(gè)字典
NSString *classKey = (__bridge NSString *)kSecClass;
//指定服務(wù)類型是普通密碼
NSString *classValue = (__bridge NSString *)kSecClassGenericPassword;
NSString *accessibleKey = (__bridge NSString *)kSecAttrAccessible;
//指定安全類型是任何時(shí)候都可以訪問
NSString *accessibleValue = (__bridge NSString *)kSecAttrAccessibleAlways;
NSString *accountKey = (__bridge NSString *)kSecAttrAccount;
//指定服務(wù)的賬戶名 可以與服務(wù)名相同 賬戶名可以對(duì)應(yīng)多個(gè)服務(wù)名
NSString *accountValue = accountId;
NSString *serviceKey = (__bridge NSString *)kSecAttrService;
//指定服務(wù)的名字 可以與服務(wù)賬戶名相同
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{
// 獲取存儲(chǔ)的數(shù)據(jù)的條件
NSMutableDictionary * saveQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 刪除舊的數(shù)據(jù)
SecItemDelete((CFDictionaryRef)saveQueryDic);
// 設(shè)置新的數(shù)據(jù)
[saveQueryDic setObject:[NSKeyedArchiver archivedDataWithRootObject:aData] forKey:(id)kSecValueData];
// 添加數(shù)據(jù)
OSStatus saveState = SecItemAdd((CFDictionaryRef)saveQueryDic, nil);
// 釋放對(duì)象
saveQueryDic = nil ;
// 判斷是否存儲(chǔ)成功
if (saveState == errSecSuccess) {
return YES;
}
return NO;
}
- (id)keychainGetDataWithAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
id idObject = nil ;
// 通過標(biāo)記獲取數(shù)據(jù)查詢條件
NSMutableDictionary * readQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 查詢結(jié)果返回到 kSecValueData (此項(xiàng)必選)
[readQueryDic setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
// 只返回搜索到的第一條數(shù)據(jù) (此項(xiàng)必選)
[readQueryDic setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
// 創(chuàng)建一個(gè)對(duì)象接受結(jié)果
CFDataRef keyChainData = nil ;
// 通過條件查詢數(shù)據(jù)
if (SecItemCopyMatching((CFDictionaryRef)readQueryDic , (CFTypeRef *)&keyChainData) == noErr){
@try {
//轉(zhuǎn)換類型
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;
// 返回?cái)?shù)據(jù)
return idObject ;
}
- (BOOL)keychainUpdataData:(id)data withAccountIdentifier:(NSString *)accountId andServiceIdentifier:(NSString *)serviceId{
// 通過標(biāo)記獲取數(shù)據(jù)更新的條件
NSMutableDictionary * updataQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 創(chuàng)建更新數(shù)據(jù)字典
NSMutableDictionary * newDic = @{}.mutableCopy;
// 存儲(chǔ)數(shù)據(jù)
[newDic setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
// 獲取存儲(chǔ)的狀態(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ù)據(jù)的查詢條件
NSMutableDictionary * deleteQueryDic = [self keychainDicWithAccountId:accountId andServiceId:serviceId];
// 刪除指定條件的數(shù)據(jù)
SecItemDelete((CFDictionaryRef)deleteQueryDic);
deleteQueryDic = nil ;
}
注:文章出處
作者:Trigger_o
鏈接:http://www.reibang.com/p/6c2265a82f7