iOS程序猿iOS DeveloperiOS进阶+实战

【iOS】指纹(面容)支付基本逻辑和适配

2018-08-06  本文已影响31人  x_code

在这边提供一些指纹和面容支付的基本思路,差异以及所遇到的坑。

一、支付逻辑基本思路

我们重点是考虑如何保证支付的安全,首先肯定不能本地存入用户的支付密码,这样在人行(中国人民银行)来检查的时候是行不通的,而且直接存密码在任何时候都是下下策。
我们应该考虑在指纹验证通过后,如何和服务端进行安全交互:
1、首先指纹或者面容通过后,我们需要和服务端进行安全环境校验,这个目的是保证当前的环境是安全的。可参考的方式有两种,第一种是用RSA加密,公钥加密私钥解密。第二种是AES加密,使用规定的key进行加解密。
2、安全环境校验通过后,再将所需的字段拼接加密后传给服务端进行校验,校验通过即可支付。这里所需的字段有一点,就是一定要保证唯一性,可以是设备id,用户id组合成的唯一id,类似于token。

二、适配面容支付

iPhoneX出了Face ID,因此我们也需要对Face ID进行适配(虽然我觉得不太安全,没有指纹有安全感)。
1、在系统API调用方面,其实是一样的,只是需要区分下何时是指纹何时是面容,以便进行图片、文字的更换,系统API提供了一个LABiometryType枚举进行类型判断,代码如下:

  LAContext *context  = [[LAContext alloc]init];
  NSError *authError = nil;
// MARK: 判断设备是否支持指纹识别
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]){
    NSString *myLocalizedReasonString;
    //系统给的系统判断API
    if (@available(iOS 11.0, *)) {
        if(context.biometryType == LABiometryTypeTouchID) {
            myLocalizedReasonString = @"请按Home键验证指纹";
        }else if (context.biometryType == LABiometryTypeFaceID){
            myLocalizedReasonString = @"请验证面容 ";
        }else{
            myLocalizedReasonString = @"请按Home键验证指纹";
        }
    }
  //iOS 11以前没有面容
    else{
        myLocalizedReasonString = @"请按Home键验证指纹";
    };
}

这里需要注意的是一定要先用 if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError])进行判断,然后再用LABiometryType取出验证类型,这在很多文章里都没有提过,也是我在开发过程中遇到的问题。还有一点就是系统提供了一个写法来判断方法支持最低的iOS系统: if (@available(iOS 11.0, )) {},以后可以不用网上的判断系统的方法了。

三、LAError枚举值说明

iOS 11新增了3个枚举,其实都是和以前的一样,只不过换了个名字:

LAErrorAuthenticationFailed, // 验证信息出错,这个时候会弹出localizedFallbackTitle的按钮  
LAErrorUserCancel // 用户取消验证
LAErrorUserFallback // 用户点击了手动输入密码的按钮
LAErrorSystemCancel // 被系统取消
 LAErrorPasscodeNotSet // 用户没有设置密码(六位数字或者四位数字那个)
LAErrorTouchIDNotAvailable // 用户设备不支持Touch ID 
LAErrorTouchIDNotEnrolled // 用户没有录入Touch ID
LAErrorTouchIDLockout // 用户错误次数被锁住了,需要解锁
LAErrorAppCancel // 在验证中被其他app终止
LAErrorInvalidContext // 这个应该是LAContext本身出错了,我还没遇到过
//这个没遇到过,估计没用了,枚举值都变成-1004了,说明被舍弃了
LAErrorNotInteractive 
//下面是iOS 11新增的
LAErrorBiometryNotAvailable //和LAErrorTouchIDNotAvailable一样枚举值都是-6
LAErrorBiometryNotEnrolled //和LAErrorTouchIDNotEnrolled一样枚举值都是-7
LAErrorBiometryLockout //和LAErrorTouchIDLockout一样枚举值都是-8

四、LAPolicy枚举值说明

LAPolicy一共有两个LAPolicyDeviceOwnerAuthenticationWithBiometricsLAPolicyDeviceOwnerAuthentication,区别是:
1、LAPolicyDeviceOwnerAuthentication是iOS 9之后的;
2、LAPolicyDeviceOwnerAuthentication是在指纹面容验证失败后(5次)第6次弹出锁屏密码验证,如果验证成功了就可以认定指纹或者面容成功了。
3、LAPolicyDeviceOwnerAuthenticationWithBiometrics则是失败5次后,第6次指纹或者面容就被锁定了,我们需要在第6次解锁指纹或者面容,代码如下:

case LAErrorTouchIDLockout:{
             dispatch_async(dispatch_get_main_queue(), ^{
                [context evaluatePolicy:kLAPolicyDeviceOwnerAuthentication localizedReason:@"验证手机锁屏密码,解锁指纹" reply:^(BOOL success, NSError * _Nullable error){
                    if (success) {
                       //重新进行指纹校验方法调用
                    }
                }];
            });
            
            break;
        }

需要注意的是用swich遍历LAError的时候,操作需要在主线程进行。

五、指纹验证示例整段代码

#pragma mark - 验证指纹
- (void)loadAuthentication{
    // 这个属性是设置指纹输入失败之后的弹出框的选项
    LAContext *context  = [[LAContext alloc]init];
    context.localizedFallbackTitle = @"输入密码";
    NSError *authError = nil;

    // MARK: 判断设备是否支持指纹识别
   
    if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]){
    NSString *myLocalizedReasonString;
    if (@available(iOS 11.0, *)) {
        if(context.biometryType == LABiometryTypeTouchID) {
            myLocalizedReasonString = @"请按Home键验证指纹";
        }else if (context.biometryType == LABiometryTypeFaceID){
            myLocalizedReasonString = @" 请验证面容ID";
        }else{
            myLocalizedReasonString = @"请按Home键验证指纹";
        }
    }
    else{
        myLocalizedReasonString = @"请按Home键验证指纹";
    };
    [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:myLocalizedReasonString reply:^(BOOL success, NSError * _Nullable error) {
        
        if(success){
            //在主线程进行
            dispatch_async(dispatch_get_main_queue(), ^{
                //认证成功
                //进行支付
            });
            
        }else{
            dispatch_async(dispatch_get_main_queue(), ^{
                
            });
            switch (error.code){
                case LAErrorAuthenticationFailed:{
                    // -1 连续三次指纹识别错误
                    dispatch_async(dispatch_get_main_queue(), ^{
                       //认证失败,请输入支付密码支付
                    });
                    break;
                }
                    
                case LAErrorUserFallback:{
                    // -3 用户选择其他验证方式
                    (点击了 context.localizedFallbackTitle = @"输入密码"这里的响应)
                    //主线程
                    dispatch_async(dispatch_get_main_queue(), ^{
                        
                    });
                    break;
                }
                    
                    // Authentication could not start, because passcode is not set on the device.
                    // -5 设备系统未设置密码
                    //没有面容ID权限(-6)
                case LAErrorTouchIDNotAvailable:{
                    dispatch_async(dispatch_get_main_queue(), ^{
                       //引导用户跳转到设置去开启
                        
                    });
                    break;
                }
                //iPhone没设置密码  
                case LAErrorPasscodeNotSet :{
                    dispatch_async(dispatch_get_main_queue(), ^{
                       //引导用户跳转到设置去开启
                        
                    });
                    break;
                }
                  //iPhone没录入指纹 
                case LAErrorTouchIDNotEnrolled:{
                    dispatch_async(dispatch_get_main_queue(), ^{
                       //引导用户跳转到设置去录入
                        
                    });
                    break;
                }
              //用户连续多次进行Touch ID验证失败,Touch ID被锁,需要用户输入密码解锁,先Touch ID验证密码
                case LAErrorTouchIDLockout:{
                    // -8 连续五次指纹识别错误,TouchID功能被锁定,下一次需要输入系统密码
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [context evaluatePolicy:kLAPolicyDeviceOwnerAuthentication localizedReason:@"验证手机锁屏密码,解锁指纹" reply:^(BOOL success, NSError * _Nullable error){
                            if (success) {
                                [self loadAuthentication];
                            }
                        }];
                    });
                    break;
                }
                    
                default:{
                  
                    break;
                }
            }
        }
    }];
}else{

    switch (authError.code){
        //没有面容ID权限(-6)
        case LAErrorTouchIDNotAvailable:{
            dispatch_async(dispatch_get_main_queue(), ^{
                //引导用户跳转到设置去开启
               
            });
            break;
        }
        case LAErrorAuthenticationFailed:{
            // -1 连续三次指纹识别错误
            dispatch_async(dispatch_get_main_queue(), ^{
               
                //认证失败,请输入支付密码支付
            });
            break;
        }
            
        case LAErrorPasscodeNotSet :{
            dispatch_async(dispatch_get_main_queue(), ^{
                //引导用户跳转到设置去开启

            });
            break;
        }
        case LAErrorTouchIDNotEnrolled:{
            dispatch_async(dispatch_get_main_queue(), ^{
                //引导用户跳转到设置去开启

            });
            break;
        }
            
        case LAErrorTouchIDLockout:{
            dispatch_async(dispatch_get_main_queue(), ^{
                [context evaluatePolicy:kLAPolicyDeviceOwnerAuthentication localizedReason:@"验证手机锁屏密码,解锁指纹" reply:^(BOOL success, NSError * _Nullable error){
                    if (success) {
                        [self loadAuthentication];
                    }
                }];
            });
            
            break;
        }
        
        default:{
            dispatch_async(dispatch_get_main_queue(), ^{
                if (@available(iOS 11.0, *)) {
                    if(context.biometryType == LABiometryTypeTouchID) {
                       //设备不支持Touch ID
                    }else if (self.myContext.biometryType == LABiometryTypeFaceID){
                        //设备不支持面容 ID
                    }else{
                      //设备不支持Touch ID
                    }
                }else{
                    //设备不支持Touch ID
                }
            });
            break;
        }
      }
    }
  }

五、总结

具体的指纹支付逻辑,加密方式,还是应该和服务端以及安卓一起讨论决定。其他就是做好异常处理,保证代码安全。
目前我还遇到了一个奇怪的bug,就是iOS 11.0系统,用户录入了指纹,但是没有将指纹或者密码用于锁屏解锁,就会每次都跳出LAErrorTouchIDLockout这个错误。试了一下,微信和支付宝一样的,也就是系统bug,其他系统正常。后续如果有其他的我还会补充。

上一篇下一篇

猜你喜欢

热点阅读