iOS指紋識(shí)別登錄流程及實(shí)現(xiàn)
最近一直在追青云志,總覺得電視劇沒有小說來的精彩。是的,大咖們演技堪稱驚艷,劇情改編也很緊湊,但不得不說很多東西單靠演是達(dá)不到的,主人公每一刻的內(nèi)心也只能在小說中才能看的貼切(為了裝X,哥不惜二百兩買了一沓正版典藏版)。
看過的童鞋知道,張小凡手中的法寶,是由攝魂與嗜血珠以張小凡精血為媒淬煉而成。而且此法寶,有一特大優(yōu)秀品質(zhì),那就是除了與張小凡有血緣關(guān)系的人之外,即便你有通天本領(lǐng)也不能操控,忠誠如此夫復(fù)何求啊,說到這里大概就扯到正題了,對(duì)的,此法寶自帶安全驗(yàn)證功能,類似我們今天的密碼校驗(yàn)與 紋識(shí)別驗(yàn)證 功能。
指紋識(shí)別簡析蘋果設(shè)計(jì)的iOS是以安全性為核心的,不管是沙盒機(jī)制,還是代碼簽名等,他們的最終目的都是為了安全。
iOS 安全架構(gòu)圖
自iPhone 5S始,蘋果公司推出了全新生物安全識(shí)別技術(shù)---指紋識(shí)別驗(yàn)證(Touch ID)。使得我們可以更快、更輕松地對(duì)設(shè)備進(jìn)行安全的訪問。可貴的是,Touch ID做到了從任意角度讀取指紋數(shù)據(jù),克服了基于密碼進(jìn)行鎖定的不便。除此之外,蘋果還加入必須進(jìn)行密碼校驗(yàn)的場景,進(jìn)一步確保安全,例如【1】:
剛開機(jī)或重啟;
超過 48 小時(shí)未解鎖設(shè)備;
設(shè)備收到了遠(yuǎn)程鎖定命令;
五次未能成功匹配指紋;
進(jìn)入Touch ID設(shè)置模塊或更新新指紋;
最重要的一點(diǎn),蘋果公司提供Touch ID給第三方應(yīng)用程序使用,程序只會(huì)收到認(rèn)證是否成功的通知,而無法訪問 Touch ID 或與已注冊(cè)指紋相關(guān)的數(shù)據(jù),這一點(diǎn)對(duì)安全而言尤為重要。
為了獲得更高的安全性,很多銀行類、支付類APP都集成了指紋、手勢(shì)等二次驗(yàn)證功能。今天我們就重點(diǎn)來談?wù)凾ouch ID集成到APP的具體流程及實(shí)現(xiàn)。
流程分析指紋登錄流程:
首次登錄.png
二次啟動(dòng)后識(shí)別登錄:
指紋驗(yàn)證登錄.png
使用過指紋登錄的朋友,大概都知道上面的流程。這個(gè)業(yè)務(wù)實(shí)現(xiàn)的難點(diǎn)在于,首次登錄成功并啟用指紋授權(quán)--->退出APP后--->二次啟動(dòng)APP,如何判斷是否要啟用指紋登錄驗(yàn)證呢?這時(shí)就需要我們對(duì)數(shù)據(jù)持久化和數(shù)據(jù)共享有較深的理解,很多APP開發(fā)者,在開發(fā) 登錄保持 的時(shí)候,大都會(huì)使用持久化數(shù)據(jù)的方式,存儲(chǔ) 成功登錄 的標(biāo)記。但對(duì)于安全性較高的APP,每次重新啟動(dòng)時(shí)都會(huì)校驗(yàn)登錄狀態(tài),單靠持久化數(shù)據(jù)是不夠的。
我的解決方案是:
通過三個(gè)數(shù)據(jù)進(jìn)行 登錄保持 ,
loginState:持久化數(shù)據(jù),用于存儲(chǔ)用戶登錄成功,未激活狀態(tài);
startAutoLoginState:持久化數(shù)據(jù),是否開啟指紋識(shí)別授權(quán);
isAppCurrentLoginState:共享數(shù)據(jù),登錄激活狀態(tài),該狀態(tài)的特點(diǎn),每次重新啟動(dòng)APP都會(huì)重新初始化數(shù)據(jù)。
首次登錄:
三個(gè)數(shù)據(jù)變化情況,
狀態(tài)loginStatestartAutoLoginStateisAppCurrentLoginState登錄之前null或NOnull或NONO登錄成功YESnull或NOYES啟用指紋授權(quán)YESYESYES不啟用授權(quán)YESNOYES二次驗(yàn)證登錄(指紋登錄):
三個(gè)數(shù)據(jù)變化情況,
如果loginState和startAutoLoginState同為YES,即可進(jìn)行指紋登錄驗(yàn)證,以下為數(shù)據(jù)變化情況;
狀態(tài)loginStatestartAutoLoginStateisAppCurrentLoginState驗(yàn)證之前YESYESNO驗(yàn)證失敗NOYESNO驗(yàn)證成功YESYESYES否則,重新登錄。
核心代碼實(shí)現(xiàn)判斷設(shè)備是否支持指紋識(shí)別
/** * 判斷設(shè)備是否支持指紋識(shí)別 */ - (IBAction)loginBtnAction:(UIButton *)sender{ [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@'loginState']; EVNHelper *helper = [EVNHelper shareHelper]; helper.isAppCurrentLoginState = YES; LAContext *context = [[LAContext alloc] init]; // 初始化上下文對(duì)象 NSError *error = nil; // 判斷設(shè)備是否支持指紋識(shí)別功能 if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) { // 支持指紋驗(yàn)證 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@'登錄成功!' message:@'是否啟用指紋登錄' preferredStyle:UIAlertControllerStyleAlert];__weak typeof (self) weakSelf = self; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@'稍后' style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@'startAutoLoginState']; weakSelf.transLoginStateBlock(); // 回傳 [self dismissViewControllerAnimated:YES completion:nil];}]; UIAlertAction *startUseAction = [UIAlertAction actionWithTitle:@'啟用' style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@'startAutoLoginState']; weakSelf.transLoginStateBlock(); // 回傳 [self dismissViewControllerAnimated:YES completion:nil];}];[alertController addAction:cancelAction];[alertController addAction:startUseAction];[self presentViewController:alertController animated:YES completion:nil]; } else {[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@'startAutoLoginState']; self.transLoginStateBlock(); // 回傳 [self dismissViewControllerAnimated:YES completion:nil]; }}
指紋登錄驗(yàn)證
/** * 指紋登錄驗(yàn)證 */ - (void)loadAuthentication{ __weak typeof(self) weakSelf = self; LAContext *myContext = [[LAContext alloc] init]; // 這個(gè)屬性是設(shè)置指紋輸入失敗之后的彈出框的選項(xiàng) myContext.localizedFallbackTitle = @'忘記密碼'; NSError *authError = nil; NSString *myLocalizedReasonString = @'請(qǐng)按住Home鍵完成驗(yàn)證'; // MARK: 判斷設(shè)備是否支持指紋識(shí)別 if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:myLocalizedReasonString reply:^(BOOL success, NSError * _Nullable error) { if(success) { NSLog(@'指紋認(rèn)證成功');weakSelf.helper.isAppCurrentLoginState = YES;weakSelf.logoutBtnAction.hidden = NO;weakSelf.userInfo.text = @'仁伯安'; } else {weakSelf.helper.isAppCurrentLoginState = NO; NSLog(@'指紋認(rèn)證失敗,%@',error.description); NSLog(@'%ld', (long)error.code); // 錯(cuò)誤碼 error.code switch (error.code){ case LAErrorAuthenticationFailed: // Authentication was not successful, because user failed to provide valid credentials { NSLog(@'授權(quán)失敗'); // -1 連續(xù)三次指紋識(shí)別錯(cuò)誤 } break; case LAErrorUserCancel: // Authentication was canceled by user (e.g. tapped Cancel button) { NSLog(@'用戶取消驗(yàn)證Touch ID'); // -2 在TouchID對(duì)話框中點(diǎn)擊了取消按鈕 } break; case LAErrorUserFallback: // Authentication was canceled, because the user tapped the fallback button (Enter Password) {[[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@'用戶選擇輸入密碼,切換主線程處理'); // -3 在TouchID對(duì)話框中點(diǎn)擊了輸入密碼按鈕 }]; } break; case LAErrorSystemCancel: // Authentication was canceled by system (e.g. another application went to foreground) { NSLog(@'取消授權(quán),如其他應(yīng)用切入,用戶自主'); // -4 TouchID對(duì)話框被系統(tǒng)取消,例如按下Home或者電源鍵 } break; case LAErrorPasscodeNotSet: // Authentication could not start, because passcode is not set on the device. { NSLog(@'設(shè)備系統(tǒng)未設(shè)置密碼'); // -5 } break; case LAErrorTouchIDNotAvailable: // Authentication could not start, because Touch ID is not available on the device { NSLog(@'設(shè)備未設(shè)置Touch ID'); // -6 } break; case LAErrorTouchIDNotEnrolled: // Authentication could not start, because Touch ID has no enrolled fingers { NSLog(@'用戶未錄入指紋'); // -7 } break; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0 case LAErrorTouchIDLockout: //Authentication was not successful, because there were too many failed Touch ID attempts and Touch ID is now locked. Passcode is required to unlock Touch ID, e.g. evaluating LAPolicyDeviceOwnerAuthenticationWithBiometrics will ask for passcode as a prerequisite 用戶連續(xù)多次進(jìn)行Touch ID驗(yàn)證失敗,Touch ID被鎖,需要用戶輸入密碼解鎖,先Touch ID驗(yàn)證密碼 { NSLog(@'Touch ID被鎖,需要用戶輸入密碼解鎖'); // -8 連續(xù)五次指紋識(shí)別錯(cuò)誤,TouchID功能被鎖定,下一次需要輸入系統(tǒng)密碼 } break; case LAErrorAppCancel: // Authentication was canceled by application (e.g. invalidate was called while authentication was in progress) 如突然來了電話,電話應(yīng)用進(jìn)入前臺(tái),APP被掛起啦'); { NSLog(@'用戶不能控制情況下APP被掛起'); // -9 } break; case LAErrorInvalidContext: // LAContext passed to this call has been previously invalidated. { NSLog(@'LAContext傳遞給這個(gè)調(diào)用之前已經(jīng)失效'); // -10 } break; #else #endif default: {[[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@'其他情況,切換主線程處理');}]; break; }} }}]; } else { NSLog(@'設(shè)備不支持指紋'); NSLog(@'%ld', (long)authError.code);weakSelf.helper.isAppCurrentLoginState = NO; switch (authError.code){ case LAErrorTouchIDNotEnrolled: { NSLog(@'Authentication could not start, because Touch ID has no enrolled fingers'); break; } case LAErrorPasscodeNotSet: { NSLog(@'Authentication could not start, because passcode is not set on the device'); break; } default: { NSLog(@'TouchID not available'); break; }} }}
參考文獻(xiàn):
【1】 iOS security guide ;
【2】 Apple Objective-C ;
【3】 Apple Swift API .
來自:http://www.jianshu.com/p/67fd93408517
相關(guān)文章: