iOS --- Touch ID指纹解锁

最近在项目中刚好用到了TouchId指纹解锁功能,之前也没有接触过,立马百度看看究竟是要如何使用,发现其实也不是很复杂。
文章后面有封装的工具方法,可以直接copy使用。

下面开始跟大家分享一下:

一、使用要点:

(1)需要导入库 LocalAuthentication.framework
(2)引入头文件 #import <LocalAuthentication/LocalAuthentication.h>
(3)指纹解锁只支持iOS 8及以上的版本
(4)如果指纹验证多次错误会被锁定,只能通过输入手机密码来重新启用。iOS 9 提供了一个方法,可以直接调起密码输入界面。
(5)所有操作必须要回到主线程,因为系统的验证是在子线程。
(6)所有的错误处理是通过一个枚举来判断处理的,具体作用看注释

二、代码要点

(1)这是判断系统版本是否大于iOS 8

if (!(MQ_CURRENT_DEVICE_SYSTEM_VERSION >= 8.0)) {
        result(NO, MQ_TOUCHID_ERROR_SYSTEM_NOT_SUPPORT);
        return;
    }

(2)判断是否可用

// 返回bool值
[authenContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]

(3)下面的就是重要的点 :

指纹解锁多次失败以后,系统会锁定TouchID硬件必须通过输入手机密码来解锁,如果系统是iOS8的话,TouchID被锁定以后只能通过重启手机来重新开启。

//系统提供的两个验证的枚举
typedef NS_ENUM(NSInteger, LAPolicy)
{
    LAPolicyDeviceOwnerAuthenticationWithBiometrics NS_ENUM_AVAILABLE(NA, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0) = kLAPolicyDeviceOwnerAuthenticationWithBiometrics,

    LAPolicyDeviceOwnerAuthentication NS_ENUM_AVAILABLE(10_11, 9_0) = kLAPolicyDeviceOwnerAuthentication

} NS_ENUM_AVAILABLE(10_10, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0);

kLAPolicyDeviceOwnerAuthentication 这个值只有在iOS9.0及以后才可以使用,利用这个值可以调出输入密码来解锁TouchID的界面。

[authenContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                  localizedReason:TOUCHID_NOTICE_MESSAGE
                            reply:^(BOOL success, NSError * _Nullable error) {
                                if (success) {
                                  //已经重新解锁Touchid
                                } 
                            }];

开始指纹解锁代码

[authenContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                  localizedReason:TOUCHID_NOTICE_MESSAGE
                            reply:^(BOOL success, NSError * _Nullable error) {
                                if (success) {
                                   // 解锁成功
                                } 
                            }];

(4)看看系统的错误枚举

typedef NS_ENUM(NSInteger, LAError)
{
    /// Authentication was not successful, because user failed to provide valid credentials.
    LAErrorAuthenticationFailed = kLAErrorAuthenticationFailed,
    
    /// Authentication was canceled by user (e.g. tapped Cancel button).
    LAErrorUserCancel = kLAErrorUserCancel,
    
    /// Authentication was canceled, because the user tapped the fallback button (Enter Password).
    LAErrorUserFallback = kLAErrorUserFallback,
    
    /// Authentication was canceled by system (e.g. another application went to foreground).
    LAErrorSystemCancel = kLAErrorSystemCancel,
    
    /// Authentication could not start, because passcode is not set on the device.
    LAErrorPasscodeNotSet = kLAErrorPasscodeNotSet,

    /// Authentication could not start, because Touch ID is not available on the device.
    LAErrorTouchIDNotAvailable NS_ENUM_DEPRECATED(10_10, 10_13, 8_0, 11_0, "use LAErrorBiometryNotAvailable") = kLAErrorTouchIDNotAvailable,

    /// Authentication could not start, because Touch ID has no enrolled fingers.
    LAErrorTouchIDNotEnrolled NS_ENUM_DEPRECATED(10_10, 10_13, 8_0, 11_0, "use LAErrorBiometryNotEnrolled") = kLAErrorTouchIDNotEnrolled,

    /// 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.
    LAErrorTouchIDLockout NS_ENUM_DEPRECATED(10_11, 10_13, 9_0, 11_0, "use LAErrorBiometryLockout")
        __WATCHOS_DEPRECATED(3.0, 4.0, "use LAErrorBiometryLockout") __TVOS_DEPRECATED(10.0, 11.0, "use LAErrorBiometryLockout") = kLAErrorTouchIDLockout,

    /// Authentication was canceled by application (e.g. invalidate was called while
    /// authentication was in progress).
    LAErrorAppCancel NS_ENUM_AVAILABLE(10_11, 9_0) = kLAErrorAppCancel,

    /// LAContext passed to this call has been previously invalidated.
    LAErrorInvalidContext NS_ENUM_AVAILABLE(10_11, 9_0) = kLAErrorInvalidContext,

    /// Authentication could not start, because biometry is not available on the device.
    LAErrorBiometryNotAvailable NS_ENUM_AVAILABLE(10_13, 11_0) __WATCHOS_AVAILABLE(4.0) __TVOS_AVAILABLE(11.0) = kLAErrorBiometryNotAvailable,

    /// Authentication could not start, because biometry has no enrolled identities.
    LAErrorBiometryNotEnrolled NS_ENUM_AVAILABLE(10_13, 11_0) __WATCHOS_AVAILABLE(4.0) __TVOS_AVAILABLE(11.0) = kLAErrorBiometryNotEnrolled,

    /// Authentication was not successful, because there were too many failed biometry attempts and
    /// biometry is now locked. Passcode is required to unlock biometry, e.g. evaluating
    /// LAPolicyDeviceOwnerAuthenticationWithBiometrics will ask for passcode as a prerequisite.
    LAErrorBiometryLockout NS_ENUM_AVAILABLE(10_13, 11_0) __WATCHOS_AVAILABLE(4.0) __TVOS_AVAILABLE(11.0) = kLAErrorBiometryLockout,
    
    /// Authentication failed, because it would require showing UI which has been forbidden
    /// by using interactionNotAllowed property.
    LAErrorNotInteractive API_AVAILABLE(macos(10.10), ios(8.0), watchos(3.0), tvos(10.0)) = kLAErrorNotInteractive,
} NS_ENUM_AVAILABLE(10_10, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0);

三、这是我封装的工具类

(1)MQTouchID.h文件

//
//  MQTouchID.h
//  Test
//
//  Created by PasserMontanus on 2018/3/21.
//  Copyright © 2018年 lgl. All rights reserved.
//
#import <Foundation/Foundation.h>

typedef NS_ENUM(NSInteger, MQ_TOUCHID_ERROR) {
    // 系统不支持 要大于 8.0
    MQ_TOUCHID_ERROR_SYSTEM_NOT_SUPPORT = 0,
    // 验证成功
    MQ_TOUCHID_ERROR_SUCCESS = 1,
    // 系统取消授权,如其他APP切入
    MQ_TOUCHID_ERROR_SYSTEM_CANCEL = -4,
    // 用户取消验证Touch ID
    MQ_TOUCHID_ERROR_USER_CANCEL = -2,
    // 用户选择输入密码
    MQ_TOUCHID_ERROR_USER_FALLBACK = -3,
    //授权失败
    MQ_TOUCHID_ERROR_FAILED = -1,
    //系统未设置密码 这个密码在用户指纹多次验证失败的时候要用他来重新激活
    MQ_TOUCHID_ERROR_PASSCODE_NOT_SET = -5,
    //设备Touch ID不可用,例如未打开
    MQ_TOUCHID_ERROR_NOT_AVAILABLE = -6,
    //设备Touch ID不可用,用户没有录入指纹
    MQ_TOUCHID_ERROR_NOT_ENROLLED = -7,
    //身份验证失败,因为它需要显示已被禁止的UI
    MQ_TOUCHID_ERROR_NOT_INTERACTIVE = -1004,
    // 设备Touch ID被锁定,输入错误的次数过多 这个时候需要密码来重新激活
    MQ_TOUCHID_ERROR_LOCKOUT = -8 ,
    //应用程序已取消身份验证
    MQ_TOUCHID_ERROR_APP_CANCEL = -9,
    // 传递给此调用的LAContext先前已失效。
    MQ_TOUCHID_ERROR_INVALID_CONTEXT = -10
};

typedef void(^MQTouchIDResult)(BOOL isSuccess, MQ_TOUCHID_ERROR error_code);

@interface MQTouchID : NSObject

/**
 开始验证 TouchID 指纹解锁 (需要导入 LocalAuthentication.framework)
 
 注意: 结果回调以后的操作都需要回到主线程 这个验证是在子线程里面执行的
 */
+ (void)validateTouchID:(MQTouchIDResult)result;

@end

(2)MQTouchID.m文件

//
//  MQTouchID.m
//  Test
//
//  Created by PasserMontanus on 2018/3/21.
//  Copyright © 2018年 lgl. All rights reserved.
//
#import "MQTouchID.h"
#import <LocalAuthentication/LocalAuthentication.h>

#define MQ_CURRENT_DEVICE_SYSTEM_VERSION ([[UIDevice currentDevice]systemVersion].doubleValue)

static NSString  * TOUCHID_NOTICE_MESSAGE          = @"通过Home键验证已有的手机指纹";
static NSString  * TOUCHID_NOTICE_RESTART_MESSAGE  = @"重新开启TouchID功能";


@implementation MQTouchID


+ (void)validateTouchID:(MQTouchIDResult)result {
    if (!(MQ_CURRENT_DEVICE_SYSTEM_VERSION >= 8.0)) {
        result(NO, MQ_TOUCHID_ERROR_SYSTEM_NOT_SUPPORT);
        return;
    }
    LAContext *authenContext = [[LAContext alloc]init];
    NSError* error = nil;
    if ([authenContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) { // 可用
        [self startValidateTouchID:authenContext result:result];
    } else { // 不可用
        MQ_TOUCHID_ERROR erroCode =  [self errorCode:error.code];
        [self processingTouchIdError:authenContext erroCode:erroCode result:result];
    }
}


+ (void)startValidateTouchID:(LAContext *)authenContext result:(MQTouchIDResult)result {
    [authenContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                  localizedReason:TOUCHID_NOTICE_MESSAGE
                            reply:^(BOOL success, NSError * _Nullable error) {
                                if (success) {
                                    result(YES,MQ_TOUCHID_ERROR_SUCCESS);
                                } else {
                                    MQ_TOUCHID_ERROR erroCode =  [self errorCode:error.code];
                                    [self processingTouchIdError:authenContext erroCode:erroCode result:result];
                                }
                            }];
}

+ (void)processingTouchIdError:(LAContext *)authenContext
                      erroCode:(MQ_TOUCHID_ERROR)erroCode
                        result:(MQTouchIDResult)result {
    
    if (@available(iOS 9.0, *)) {
        if (erroCode == MQ_TOUCHID_ERROR_LOCKOUT) {
            [authenContext evaluatePolicy:LAPolicyDeviceOwnerAuthentication
                          localizedReason:TOUCHID_NOTICE_RESTART_MESSAGE
                                    reply:^(BOOL success, NSError * _Nullable error) {
                                        if (success) {
                                            [self validateTouchID:result];
                                        } else {
                                            result(NO, MQ_TOUCHID_ERROR_USER_CANCEL);
                                        }
                                    }];
        
        } else {
            result(NO, erroCode);
        }
    } else {
        result(NO, erroCode);
    }
}

+ (MQ_TOUCHID_ERROR)errorCode:(NSInteger )errorCode {
    switch (errorCode) {
        case LAErrorSystemCancel:{ // 系统取消授权,如其他APP切入
            return MQ_TOUCHID_ERROR_SYSTEM_CANCEL;
            break;
        }
        case LAErrorUserCancel:{ // 用户取消验证Touch ID
            return MQ_TOUCHID_ERROR_USER_CANCEL;
            break;
        }
        case LAErrorUserFallback:{ // 用户选择输入密码
            return MQ_TOUCHID_ERROR_USER_FALLBACK;
            break;
        }
        case LAErrorAuthenticationFailed:{ // //授权失败
            return MQ_TOUCHID_ERROR_FAILED;
            break;
        }
        case LAErrorPasscodeNotSet: { //系统未设置密码 这个密码在用户指纹多次验证失败的时候要用他来重新激活
            return MQ_TOUCHID_ERROR_PASSCODE_NOT_SET;
            break;
        }
        case LAErrorTouchIDNotAvailable: { //设备Touch ID不可用,例如未打开
            return MQ_TOUCHID_ERROR_NOT_AVAILABLE;
            break;
        }
        case LAErrorTouchIDNotEnrolled: { //设备Touch ID不可用,用户没有录入指纹
            return MQ_TOUCHID_ERROR_NOT_ENROLLED;
            break;
        }
        case LAErrorNotInteractive: { //   身份验证失败,因为它需要显示已被禁止的UI
            return MQ_TOUCHID_ERROR_NOT_INTERACTIVE;
            break;
        }
        default:{
            if (@available(iOS 9.0, *)) {
                if (errorCode == LAErrorTouchIDLockout) { // 设备Touch ID被锁定,输入错误的次数过多 这个时候需要密码来重新激活
                    return MQ_TOUCHID_ERROR_LOCKOUT;
                    break;
                } else {
                    if (errorCode == LAErrorInvalidContext) {
                        return MQ_TOUCHID_ERROR_INVALID_CONTEXT;
                        break;
                    } else if (errorCode == LAErrorAppCancel) {
                        return MQ_TOUCHID_ERROR_APP_CANCEL;
                        break;
                    } else {
                        return MQ_TOUCHID_ERROR_FAILED;
                        break;
                    }
                }
            } else {
                return MQ_TOUCHID_ERROR_FAILED;
                break;
            }
        }
    }
}

@end

四、最后结语

(1)最后的结果回调以后的操作一定要在主线程,系统验证在子线程进行
(2)以上就是我的一点小总结,欢迎大家指正。

posted on 2018-03-21 19:07  ljmaque  阅读(1129)  评论(0编辑  收藏  举报

导航