點擊這里:KeyChain and TouchID Demo
首先需要引入一個庫#import <LocalAuthentication/LAContext.h>,官方的一個用于TouchID安全驗證登陸的Framework,首先需要驗證是否支持TouchID登陸:
// 判斷是否支持TouchID登陸
- (BOOL)canEvaluatePolicy
{
LAContext *context = [[LAContext alloc] init];
NSError *error;
return [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&error];
}
如果不支持的話,就需要走自己APP中得密碼驗證方法來進(jìn)行驗證登陸;
如果支持的話,就調(diào)起TouchID驗證的頁面:
- (void)evaluatePolicy
{
LAContext *context = [[LAContext alloc] init];
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"使用TouchID進(jìn)入APP首頁" reply:^(BOOL success, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (success) {
// ...
}
else {
// ...
}
});
}];
}
注意
reply這個block中得代碼是在異步線程中執(zhí)行的,如果有需要的話,必須得拋到主線程中去執(zhí)行一些東西。TouchID的失敗次數(shù)是有限制的,從開始算,如果失敗了三次,系統(tǒng)就會自動的退出TouchID的驗證界面,并在
replyblock中返回失敗的信息,這時還可以調(diào)起TouchID的驗證,此時如果失敗兩次即會退出驗證界面。
在這種情況下,如果還想用TouchID驗證,則必須輸入手機的登陸密碼才能繼續(xù)驗證。
需要注意的是,這個失敗的次數(shù)在iOS設(shè)備中所有的APP是累加起來的。
KeyChain
keychain是作為蘋果中對于數(shù)據(jù)安全存儲的一個地方,類似于NSUserDefaults,但又不同于NSUserDefaults。
對于keychain來說,需要注意的方法有以下四個:
SecItemAddSecItemUpdateSecItemCopyMatchingSecItemDelete
注意
在使用的時候一定要注意
KeyChain操作的返回值。
KeyChain操作中得一些參數(shù)的設(shè)定(一些相關(guān)參數(shù)的說明):
- (NSMutableDictionary *)getKeychainQueryWithService:(NSString *)service
{
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kSecClassGenericPassword, (__bridge id)kSecClass,
service, (__bridge id)kSecAttrService,
service, (__bridge id)kSecAttrAccount,
(__bridge id)kSecAttrAccessibleAfterFirstUnlock, (__bridge id)kSecAttrAccessible,
nil];
if (self.accessGroupName != nil) {
[query setObject:self.accessGroupName forKey:(__bridge id)kSecAttrAccessGroup];
}
return query;
}
SecItemAdd
用于將個人數(shù)據(jù)條目保存到keychain中。
- 方法原型:
OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef * result);-
attributes: 添加屬性的設(shè)置。 -
result: 可以用與獲取返回值,&res
-
- (OSStatus)saveObject:(id)object forService:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQueryWithService:service];
// 需要檢查數(shù)據(jù)的存在性,如果存在的話,就用SecItemUpdate更新,否則就添加
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:object] forKey:(__bridge id)kSecValueData];
return SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
}
SecItemUpdate
用于更新KeyChain中保存的數(shù)據(jù)。
- 方法原型:
OSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);-
query: 查找屬性的設(shè)置 -
attributesToUpdate: 要更新的數(shù)據(jù)
-
- (OSStatus)updateService:(NSString *)service withObject:(id)object
{
NSDictionary *keychainQuery = [self getKeychainQueryWithService:service];
NSDictionary *changes = @{
(__bridge id)kSecValueData : [NSKeyedArchiver archivedDataWithRootObject:object]
};
return SecItemUpdate((__bridge CFDictionaryRef)keychainQuery, (__bridge CFDictionaryRef)changes);
}
SecItemCopyMatching
用于獲取保存在KeyChain中得數(shù)據(jù)。
- 方法原型:
OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef * result);-
query: 保存在KeyChain中得數(shù)據(jù)信息。 -
result: 從KeyChain中取得的數(shù)據(jù)。
-
- (id)loadObjectForService:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQueryWithService:service];
[keychainQuery setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[keychainQuery setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
CFDataRef dataRef = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&dataRef);
id results = nil;
if (status == errSecSuccess) {
@try {
results = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)dataRef];
}
@catch (NSException *exception) {
NSLog(@"%@", exception);
}
@finally {
NSLog(@"finally");
}
}
if (dataRef) {
CFRelease(dataRef);
}
return results;
}
SecItemDelete
用于刪除保存在KeyChain中得數(shù)據(jù)。
- 方法原型:
OSStatus SecItemDelete(CFDictionaryRef query)-
query:要刪除的數(shù)據(jù)的信息。
-
- (void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQueryWithService:service];
SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
}

TIDLogin
附:
OSStatus狀態(tài)碼
CF_ENUM(OSStatus)
{
errSecSuccess = 0, /* No error. */
errSecUnimplemented = -4, /* Function or operation not implemented. */
errSecIO = -36, /*I/O error (bummers)*/
errSecOpWr = -49, /*file already open with with write permission*/
errSecParam = -50, /* One or more parameters passed to a function where not valid. */
errSecAllocate = -108, /* Failed to allocate memory. */
errSecUserCanceled = -128, /* User canceled the operation. */
errSecBadReq = -909, /* Bad parameter or invalid state for operation. */
errSecInternalComponent = -2070,
errSecNotAvailable = -25291, /* No keychain is available. You may need to restart your computer. */
errSecDuplicateItem = -25299, /* The specified item already exists in the keychain. */
errSecItemNotFound = -25300, /* The specified item could not be found in the keychain. */
errSecInteractionNotAllowed = -25308, /* User interaction is not allowed. */
errSecDecode = -26275, /* Unable to decode the provided data. */
errSecAuthFailed = -25293, /* The user name or passphrase you entered is not correct. */
};