最近在app上加上了指纹和手势验证功能,功能告一段落,简单阐述下。大家可以更好的集成到自己的项目中。
- 1、指纹
我把指纹的验证和登录集成成一个service,看下代码然后解释吧:
@interface KZWFingerprintService : NSObject
+ (void)openFingerprintService:(UIViewController *)controller tableView:(UITableView *)tableView;
+ (void)fingerprintService:(UIViewController *)controller;
@end
一个service,2个方法,一个验证是否开启,一个登录。
- 验证
+ (void)openFingerprintService:(UIViewController *)controller tableView:(UITableView *)tableView{
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"FINGERPRINTLOGIN"]) {
LAContext *context = [[LAContext alloc] init];
NSError *error = nil;
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"确定关闭指纹密码?" message:@"关闭后将不能用指纹快捷登录" preferredStyle:1];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"不了" style:UIAlertActionStyleCancel handler:nil];
UIAlertAction *sureAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:@"验证touchID"
reply:^(BOOL success, NSError *error) {
if (success) {
[[NSUserDefaults standardUserDefaults]setBool:NO forKey:@"FINGERPRINTLOGIN"];
dispatch_async(dispatch_get_main_queue(), ^{
[KZWTosatView showToastWithMessage:@"指纹登录关闭" view:nil];
[tableView reloadData];
});
} else {
switch (error.code) {
case LAErrorUserCancel: //用户取消验证Touch ID
{
}
break;
case LAErrorTouchIDLockout: //连续五次指纹识别错误,下一次需要输入系统密码
{
[context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:@"密码解锁" reply:^(BOOL success, NSError * _Nullable error){
}];
}
break;
case LAErrorUserFallback: // 用户选择输入密码
{
[[ELMRouter sharedRouter] open:LPD_ROUTER_URL(@"KZWMobileLoginViewController", (@{
@"fromGesturelogin": @(2) }))
animated:YES
showStyle:ELMPageShowStyleModal];
}
break;
case LAErrorAuthenticationFailed:
{
dispatch_async(dispatch_get_main_queue(), ^{
[KZWTosatView showToastWithMessage:@"失败次数过多,请谨慎操作" view:controller.view];
});
}
break;
default:
{
dispatch_async(dispatch_get_main_queue(), ^{
[KZWTosatView showToastWithMessage:@"验证失败" view:nil];
});
}
break;
}
}
}];
}];
[alert addAction:cancelAction];
[alert addAction:sureAction];
[controller presentViewController:alert animated:YES completion:nil];
}else {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"设备不支持指纹识别" message:@"账号登录来关闭指纹解锁" preferredStyle:1];
UIAlertAction *sure = [UIAlertAction actionWithTitle:@"登录" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[[ELMRouter sharedRouter] open:LPD_ROUTER_URL(@"KZWMobileLoginViewController", (@{
@"fromGesturelogin": @(2) }))
animated:YES
showStyle:ELMPageShowStyleModal];
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"稍后" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:sure];
[alert addAction:cancelAction];
[controller presentViewController:alert animated:YES completion:nil];
}
return;
}
LAContext *context = [[LAContext alloc] init];
NSError *error = nil;
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"是否开启指纹登录?" message:@"登录更加方便快捷" preferredStyle:1];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"稍后" style:UIAlertActionStyleCancel handler:nil];
UIAlertAction *sureAction = [UIAlertAction actionWithTitle:@"启用" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:@"验证touchID"
reply:^(BOOL success, NSError *error) {
if (success) {
[[NSUserDefaults standardUserDefaults]setBool:YES forKey:@"FINGERPRINTLOGIN"];
dispatch_async(dispatch_get_main_queue(), ^{
[KZWTosatView showToastWithMessage:@"验证成功" view:nil];
[tableView reloadData];
});
} else {
switch (error.code) {
case LAErrorUserCancel: //用户取消验证Touch ID
{
}
break;
case LAErrorTouchIDLockout: //连续五次指纹识别错误,下一次需要输入系统密码
{
[context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:@"密码解锁" reply:^(BOOL success, NSError * _Nullable error){
}];
}
break;
case LAErrorAuthenticationFailed:
{
dispatch_async(dispatch_get_main_queue(), ^{
[KZWTosatView showToastWithMessage:@"失败次数过多,请谨慎操作" view:controller.view];
});
}
break;
default:
{
dispatch_async(dispatch_get_main_queue(), ^{
[KZWTosatView showToastWithMessage:@"验证失败" view:nil];
});
}
break;
}
}
}];
}];
[alert addAction:cancelAction];
[alert addAction:sureAction];
[controller presentViewController:alert animated:YES completion:nil];
}else {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"设备不支持指纹识别" message:@"是否开启手势密码" preferredStyle:1];
UIAlertAction *sure = [UIAlertAction actionWithTitle:@"是" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
GestureViewController *gestureVc = [[GestureViewController alloc] init];
gestureVc.type = GestureViewControllerTypeSetting;
[controller.navigationController pushViewController:gestureVc animated:YES];
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"稍后" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:sure];
[alert addAction:cancelAction];
[controller presentViewController:alert animated:YES completion:nil];
}
}
这段代码有点长,其实写好点是可以拆成2 个方法,因为一个是验证开启,一个验证关闭。传了2个参数,一个controller,一个tableview,目的一个是跳转和刷新当前tableview。
第一个判断[[NSUserDefaults standardUserDefaults] boolForKey:@"FINGERPRINTLOGIN"]就是判断是否开启指纹识别,是的话就是关闭,否的话就是开启。里面的逻辑[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:@"验证touchID"
reply:^(BOOL success, NSError *error) 这个方法就是核心方法,验证指纹,处理的核心逻辑,成功,指纹开启,失败,其中重点是失败3次LAErrorAuthenticationFailed,失败5次LAErrorTouchIDLockout,用户点击输入密码LAErrorUserFallback,用户取消LAErrorUserCancel的处理。特殊的一点是在取消指纹的时候用户如果关闭了指纹会直接不弹框我做的处理是弹UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"设备不支持指纹识别" message:@"账号登录来关闭指纹解锁" preferredStyle:1];这样不会死循环关不了。
- 登录
+ (void)fingerprintService:(UIViewController *)controller {
LAContext *context = [[LAContext alloc] init];
NSError *error = nil;
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:@"指纹登录"
reply:^(BOOL success, NSError *error) {
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissModalStack:controller];
});
});
} else {
switch (error.code) {
case LAErrorUserCancel: //用户取消验证Touch ID
{
}
break;
case LAErrorTouchIDLockout: //连续五次指纹识别错误
{
[context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:@"密码解锁" reply:^(BOOL success, NSError * _Nullable error){
}];
}
break;
case LAErrorUserFallback: // 用户选择输入密码
{
[ELMKeychainUtil deleteAllInfoInKeyChain];
[[ELMRouter sharedRouter] open:@"KZWMobileLoginViewController" animated:YES showStyle:ELMPageShowStyleModal];
}
break;
case LAErrorAuthenticationFailed:
{
dispatch_async(dispatch_get_main_queue(), ^{
[KZWTosatView showToastWithMessage:@"失败次数过多,请谨慎操作" view:controller.view];
});
}
break;
default:
{
dispatch_async(dispatch_get_main_queue(), ^{
[KZWTosatView showToastWithMessage:@"验证失败" view:controller.view];
});
}
break;
}
}
}];
}else {
dispatch_async(dispatch_get_main_queue(), ^{
[KZWTosatView showToastWithMessage:@"设备不支持指纹识别" view:controller.view];
});
}
}
核心逻辑和验证的差不多,不懂的看代码哈。
- 2、手势
手势的核心我用的是第三方的基础上进行二次开发,这个网上很多下个差不多的改下就可以了。
说下逻辑处理:重点是在切换登录方式的时候,因为有的不在一条线上,push和pop的混用会导致你切的时候有一点麻烦。这个时候你就需要route这个强大的东西了,然后就是你各种pop和push 的时候的栈的释放,我用的这个方法:
+(void)dismissModalStack:(UIViewController *)vcr {
UIViewController *vc = vcr.presentingViewController;
while (vc.presentingViewController) {
vc = vc.presentingViewController;
}
[vc dismissViewControllerAnimated:YES completion:NULL];
}
可以直接全部释放。
开启手势:
- (void)gestureSetting {
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GESTURELOGIN"]) {
GestureVerifyViewController *gestureVerifyVc = [[GestureVerifyViewController alloc] init];
[self.navigationController pushViewController:gestureVerifyVc animated:YES];
return;
}
GestureViewController *gestureVc = [[GestureViewController alloc] init];
gestureVc.type = GestureViewControllerTypeSetting;
[self.navigationController pushViewController:gestureVc animated:YES];
}
一个判断是否开启手势,没开启跳开启,开启跳验证关闭。
修改手势:
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GESTURELOGIN"]) {
GestureVerifyViewController *gestureVerifyVc = [[GestureVerifyViewController alloc] init];
gestureVerifyVc.isToSetNewGesture = YES;
[self.navigationController pushViewController:gestureVerifyVc animated:YES];
}else {
[KZWTosatView showToastWithMessage:@"请先设置手势密码" view:self.view];
}
手势登录:
[[ELMRouter sharedRouter] open:@"GestureViewController" animated:YES showStyle:ELMPageShowStyleModal];
一般的场景有2种,直接进来就验证那直接把这段代码写在appdelegate的didFinishLaunchingWithOptions就好了:
if ([ELMKeychainUtil valueInKeyChainForKey:KZWFINANCIALLOGINTOKEN]) {
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"FINGERPRINTLOGIN"]) {
[[ELMRouter sharedRouter] open:@"KZWFingerprintLoginViewController" animated:YES showStyle:ELMPageShowStyleModal];
}else if([[NSUserDefaults standardUserDefaults] boolForKey:@"GESTURELOGIN"]){
[[ELMRouter sharedRouter] open:@"GestureViewController" animated:YES showStyle:ELMPageShowStyleModal];
}
}
如果是在个人中心的时候才验证,我的做法是appdelegate里给个参数 @property (assign, nonatomic) BOOL needCheck;在didFinishLaunchingWithOptions方法里赋值yes,然后再个人中心里判断yes就走上面的代码然后needCheck赋值为no,就好了。
有什么问题欢迎交流。
有个问题需要注意的是,在有指纹,手势,面部识别页面弹出时,你的广告、弹框,这些应该是加到你的主页的rootViewController上,而不是直接在appdelegate里判断因为这样会弹到引导页和面部识别页面上,如果识别过了,你的弹层也就释放了。