目前App登录方式主要有:
- 数字密码登录
- 手势登录
指纹登录
- 声音识别
- 人脸识别(刷脸登录)
- 虹膜识别
苹果从 iPhone 5s 开始支持指纹识别,iPhone X 开始支持面容识别,从 iOS 8 开始,指纹(面容)的 api 开始对开发者开放,指纹识别与面容识别共用同一套 api。
据 Apple 官方声称,指纹识别是生物真皮层识别,只能识别活着的生物,而且按压式的指纹识别有着更高的识别率和识别速度,面容识别是通过 iPhone X 的前置深感相机 3D 扫描用户脸部特征。
iPhone 指纹(面容)识别所需的环境:
- iPhone 5s及以上设备,且指纹识别速度是随着设备逐步提升的;
- iOS 8.0及以上系统版本;如果要做指纹支付,需要注意指纹库删减更换的漏洞,因此需要监测指纹库的Hash值(用于判断指纹库是否有变化),需要iOS 9以上版本;
- 设备状态亦需支持;包括Touch ID未损坏,系统开启数字密码,需要提前录入系统指纹等。
指纹系统 API 介绍
1、 首先导入包含指纹识别的类 #import <LocalAuthentication/LocalAuthentication.h>
;
2、 判断设备是否支持指纹识别的接口canEvaluatePolicy:
- (BOOL)canEvaluatePolicy:(LAPolicy)policy error:(NSError * __autoreleasing *)error __attribute__((swift_error(none)));
3、 调起指纹登录窗口,进行指纹识别的接口evaluatePolicy:
- (void)evaluatePolicy:(LAPolicy)policy
localizedReason:(NSString *)localizedReason
reply:(void(^)(BOOL success, NSError * __nullable error))reply;
指纹的识别错误处理
指纹识别,识别成功只有一种可能,而识别失败情况很多,所以主要需要处理的是各种失败
的情况。
一、 判断设备是否支持Touch ID
在很多情况下,都需要先判断系统是否支持指纹,调用系统APIcanEvaluatePolicy:
判断设备是否支持,以下为几种常见情况:
- 系统指纹未录入;
- 系统未设置密码;
- 指纹设备损坏不可用;
- 设备版本低于iOS 8.0;
- 系统临时禁用指纹或者面容;
- iPhone X 以上设备用户可手动关闭 app 面容。
提示1:如果连续几次输入错误,系统会临时禁用指纹识别,此时返回的也是 NO,直到正确输入一次系统密码会自动启用Touch ID;不同iOS版本,处理方式可能有所不同。 提示2:iPhone X 以上设备,用户可在 app 权限设置界面手动关闭面容。
二、 判断设备是否支持Touch ID的示例代码
可采用代码封装的方式,封装系统Api,然后根据返回的错误值不同,有选择的分类处理失败的情况即可。
//创建LAContext
LAContext* context = [[LAContext alloc] init];
NSError* error = nil;
//首先使用canEvaluatePolicy 判断设备支持状态
if (![context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]){
//不支持指纹识别的三种情况,失败可采用Block回调的方式,把错误信息传递过去
switch (error.code) {
case LAErrorTouchIDNotEnrolled:
{
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(@"指纹未录入或系统未设置密码");
});
break;
}
case LAErrorPasscodeNotSet:
{
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(@"系统未设置密码");
});
break;
}
default:
{
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(@"指纹不可用");
});
break;
}
}
}else{
//iOS8.0后才支持指纹识别接口
if ([UIDevice currentDevice].systemVersion.floatValue < 8.0) {
错误原因:(@"系统版本低,不支持");
}else{
指纹可用,可以调起指纹登录弹窗
}
}
调用指纹验证弹窗
在调用弹窗时,仍然需要注意错误处理,个别特殊情况仍会出现系统不支持指纹的情况,例如多次验证失败会临时禁用,用户删除了系统密码等。
调用系统APIevaluatePolicy:
主要会出现的情况:
- 指纹未录入或系统未设置密码
- 系统未设置密码
- 系统版本低,不支持
- 指纹验证取消(包括用户点击取消按钮,系统中断取消(例如接到来电,其他App切入))
- 指纹验证几次后失败
- 系统未设置密码
- 设备指纹不可用
- 用户选择点击指纹弹窗右边按钮
指纹弹窗窗口说明:
- 指纹登录窗口为系统窗口,且优先级很高,几乎能够覆盖到其他任何窗口的上面。
- 指纹验证的回调reply默认在子线程执行,如果需要刷新UI,一定要切换到主线程执行,否则会有卡顿
- 如果指纹弹出有明显延迟卡顿现象,注意查看系统版本是否为iOS 10.1,此为系统版本原因,对比其他App和其他系统版本即可发现。
指纹弹窗按钮设置
- 通过设置LAContext的localizedFallbackTitle字符串,可设置弹窗下方的选择按钮
- 其中指纹弹窗第一次弹出时,只有一个取消按钮,验证失败1次后才显示你设置的按钮,按钮标题为你设置的字符串
- 如果不设置,在输错错误的指纹之后会显示“输入密码”选项,点击会提示输入系统密码
- 如果连续输入错误,依然不想显示除
取消
外的其他按钮,可设置context.localizedFallbackTitle = @"";
即可。
指纹库的Hash值
- 如果安全级别要求较高,防止有人知道手机系统密码,添加自己的指纹到指纹库,从而进行App的操作,可在开启指纹时,指纹验证成功后,保存指纹库的Hash值。
- 苹果一向提倡保护用户隐私,此Hash值记录的并不是用户指纹,而是当前指纹库的Hash值,即记录的是指纹库整体的特征,如果指纹库有删除/增加/更改等任何变化,该Hash值都会变化。
- 感兴趣的小伙伴,可以查询一下Hash值,Hash值和MD5也经常用来校验文件的完整性。
- 注意的是,Hash值为一串字符串,且在iOS 9.0之后才支持,所以如果用到此值,你App的指纹登录/支付功能需要判断系统版本>9.0
用户设置按钮的回调
其中一种验证失败的情况为LAErrorUserFallback,此为用户点击了设置的context.localizedFallbackTitle,可根据自身需要,切换到手势或数字密码等。
指纹验证弹窗示例代码:
可采用代码封装的方式,封装系统Api,然后根据返回的错误值不同,有选择的分类处理失败的情况即可。
//创建LAContext
LAContext* context = [[LAContext alloc] init];
//localizedFallbackTitle 默认不设置的话,在输错错误的指纹之后会显示“输入密码”选项,如果出入空字符串@""将不显示这个选项
context.localizedFallbackTitle = fallbackTitle;
//验证指纹识别
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:Subtitle reply:^(BOOL success, NSError * _Nullable error) {
if (success) {
//如果安全级别要求较高,可在此处保存当前的指纹库的hash值为全局临时变量
//此临时变量为指纹库的hash值,如果指纹有变化,则用其他方式登录成功后,更新服务器为此最新的指纹信息
//比较,注意:evaluatedPolicyDomainState是在iOS9后出现,故指纹hash需要在iOS9后才能使用
context.evaluatedPolicyDomainState;
//验证成功,则执行刷新UI,主线程执行
dispatch_async(dispatch_get_main_queue(), ^{
//刷新UI,主线程执行
});
}else{
//验证失败
switch (error.code) {
case LAErrorSystemCancel:
{
//系统取消授权,如其他APP切入,接到电话等情况
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(@"指纹验证取消");
});
break;
}
case LAErrorUserCancel:
{
//用户取消验证Touch ID;
//⚠️坑:iOS9增加invalidate接口,可用代码取消弹窗,iOS9版本之前通过代码无法使弹窗消失
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(@"指纹验证取消");
});
break;
}
case LAErrorAuthenticationFailed:
{
//授权失败,3次尝试验证后,授权失败调用
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(@"指纹验证失败");
});
break;
}
case LAErrorPasscodeNotSet:
{
//系统未设置密码
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(@"系统未设置密码");
});
break;
}
case LAErrorTouchIDNotAvailable:
{
//设备Touch ID不可用,例如未打开
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(@"设备指纹不可用");
});
break;
}
case LAErrorTouchIDNotEnrolled:
{
//设备Touch ID不可用,用户未录入
//指纹未录入或系统未设置密码
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(@"指纹未录入或系统未设置密码");
});
break;
}
case LAErrorUserFallback:
{
//用户选择手动输入密码,根据右边的文字判断怎么跳转
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(fallbackTitle);
});
break;
}
default:
{
//例如连续验证指纹失败验证手机密码是,用户取消验证
dispatch_async(dispatch_get_main_queue(), ^{
//错误原因说明:(@"指纹验证失败");
});
break;
}
}
}
}];
如果您觉得有所帮助,请在GitHub上赏个Star ⭐️,您的鼓励是我前进的动力