赞
踩
flutter开发实战-指纹、面容ID验证插件实现
在iOS开发中,经常出现需要指纹、面容ID验证的功能。
指纹、面容ID是一种基于用生物识别技术,通过扫描用户的面部特征来验证用户身份。
在iOS中实现指纹、面容ID验证功能,步骤如下
在info.plist中配置允许访问FACE ID权限
<key>NSFaceIDUsageDescription</key>
<string>允许设备访问Face ID</string>
在代码中引入#import <LocalAuthentication/LocalAuthentication.h>
具体代码实现如下
SDAuthID.h
#import <Foundation/Foundation.h> #import <LocalAuthentication/LocalAuthentication.h> /** * 设备支持的生物验证方式 */ typedef NS_ENUM(NSUInteger, SDAuthIDSupportType) { /** * 支持TouchID验证 */ SDAuthIDSupportTypeTouchID = 1, /** * 支持FaceID验证 */ SDAuthIDSupportTypeFaceID, /** * 不支持支持验证 */ SDAuthIDSupportTypeNone, }; /** * TouchID/FaceID 状态 */ typedef NS_ENUM(NSUInteger, SDAuthIDState){ /** * 当前设备不支持TouchID/FaceID */ SDAuthIDStateNotSupport = 0, /** * TouchID/FaceID 验证成功 */ SDAuthIDStateSuccess = 1, /** * TouchID/FaceID 验证失败 */ SDAuthIDStateFail = 2, /** * TouchID/FaceID 被用户手动取消 */ SDAuthIDStateUserCancel = 3, /** * 用户不使用TouchID/FaceID,选择手动输入密码 */ SDAuthIDStateInputPassword = 4, /** * TouchID/FaceID 被系统取消 (如遇到来电,锁屏,按了Home键等) */ SDAuthIDStateSystemCancel = 5, /** * TouchID/FaceID 无法启动,因为用户没有设置密码 */ SDAuthIDStatePasswordNotSet = 6, /** * TouchID/FaceID 无法启动,因为用户没有设置TouchID/FaceID */ SDAuthIDStateTouchIDNotSet = 7, /** * TouchID/FaceID 无效 */ SDAuthIDStateTouchIDNotAvailable = 8, /** * TouchID/FaceID 被锁定(连续多次验证TouchID/FaceID失败,系统需要用户手动输入密码) */ SDAuthIDStateTouchIDLockout = 9, /** * 当前软件被挂起并取消了授权 (如App进入了后台等) */ SDAuthIDStateAppCancel = 10, /** * 当前软件被挂起并取消了授权 (LAContext对象无效) */ SDAuthIDStateInvalidContext = 11, /** * 系统版本不支持TouchID/FaceID (必须高于iOS 8.0才能使用) */ SDAuthIDStateVersionNotSupport = 12 }; typedef void (^SDAuthIDStateBlock)(SDAuthIDState state, NSError *error); @interface SDAuthID : NSObject + (instancetype)sharedInstance; /** 判断设备支持哪种认证方式 TouchID & FaceID */ - (SDAuthIDSupportType)canSupportBiometrics; /** * 启动TouchID/FaceID进行验证 * @param description TouchID/FaceID显示的描述 * @param localizedFallbackTitle 错误描述 * @param block 回调状态的block */ - (void)showAuthIDWithDescription:(NSString *)description localizedFallbackTitle:(NSString *)localizedFallbackTitle block:(SDAuthIDStateBlock)block; /** * 启动TouchID * @param description TouchID/FaceID显示的描述 * @param localizedFallbackTitle 错误描述 * @param block 回调状态的block */ - (void)showAuthTouchIDWithDescription:(NSString *)description localizedFallbackTitle:(NSString *)localizedFallbackTitle block:(SDAuthIDStateBlock)block; /** * 启动TouchID * @param description TouchID/FaceID显示的描述 * @param localizedFallbackTitle 错误描述 * @param block 回调状态的block */ - (void)showAuthFaceIDWithDescription:(NSString *)description localizedFallbackTitle:(NSString *)localizedFallbackTitle block:(SDAuthIDStateBlock)block; @end
实现的代码
SDAuthID.m
#import "SDAuthID.h" @implementation SDAuthID + (instancetype)sharedInstance { static SDAuthID *instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[SDAuthID alloc] init]; }); return instance; } /** * 启动TouchID/FaceID进行验证 * @param description TouchID/FaceID显示的描述 * @param localizedFallbackTitle 错误描述 * @param block 回调状态的block */ - (void)showAuthIDWithDescription:(NSString *)description localizedFallbackTitle:(NSString *)localizedFallbackTitle block:(SDAuthIDStateBlock)block { if (NSFoundationVersionNumber < NSFoundationVersionNumber_iOS_8_0) { dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"系统版本不支持TouchID/FaceID (必须高于iOS 8.0才能使用)"); block(SDAuthIDStateVersionNotSupport, nil); }); return; } LAContext *context = [[LAContext alloc] init]; // 认证失败提示信息,为 @"" 则不提示 context.localizedFallbackTitle = localizedFallbackTitle; NSError *error = nil; // LAPolicyDeviceOwnerAuthenticationWithBiometrics: 用TouchID/FaceID验证 // LAPolicyDeviceOwnerAuthentication: 用TouchID/FaceID或密码验证, 默认是错误两次或锁定后, 弹出输入密码界面 if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&error]) { [context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:description reply:^(BOOL success, NSError * _Nullable error) { if (success) { dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID/FaceID 验证成功"); block(SDAuthIDStateSuccess, error); }); } else if(error){ if (@available(iOS 11.0, *)) { switch (error.code) { case LAErrorAuthenticationFailed:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID/FaceID 验证失败"); block(SDAuthIDStateFail, error); }); break; } case LAErrorUserCancel:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID/FaceID 被用户手动取消"); block(SDAuthIDStateUserCancel, error); }); } break; case LAErrorUserFallback:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"用户不使用TouchID/FaceID,选择手动输入密码"); block(SDAuthIDStateInputPassword, error); }); } break; case LAErrorSystemCancel:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID/FaceID 被系统取消 (如遇到来电,锁屏,按了Home键等)"); block(SDAuthIDStateSystemCancel, error); }); } break; case LAErrorPasscodeNotSet:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID/FaceID 无法启动,因为用户没有设置密码"); block(SDAuthIDStatePasswordNotSet, error); }); } break; case LAErrorBiometryNotEnrolled:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID/FaceID 无法启动,因为用户没有设置TouchID/FaceID"); block(SDAuthIDStateTouchIDNotSet, error); }); } break; case LAErrorBiometryNotAvailable:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID/FaceID 无效"); block(SDAuthIDStateTouchIDNotAvailable, error); }); } break; case LAErrorBiometryLockout:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID/FaceID 被锁定(连续多次验证TouchID/FaceID失败,系统需要用户手动输入密码)"); block(SDAuthIDStateTouchIDLockout, error); }); } break; case LAErrorAppCancel:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"当前软件被挂起并取消了授权 (如App进入了后台等)"); block(SDAuthIDStateAppCancel, error); }); } break; case LAErrorInvalidContext:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"当前软件被挂起并取消了授权 (LAContext对象无效)"); block(SDAuthIDStateInvalidContext, error); }); } break; default: break; } } else { // iOS 11.0以下的版本只有 TouchID 认证 switch (error.code) { case LAErrorAuthenticationFailed:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID 验证失败"); block(SDAuthIDStateFail, error); }); break; } case LAErrorUserCancel:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID 被用户手动取消"); block(SDAuthIDStateUserCancel, error); }); } break; case LAErrorUserFallback:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"用户不使用TouchID,选择手动输入密码"); block(SDAuthIDStateInputPassword, error); }); } break; case LAErrorSystemCancel:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID 被系统取消 (如遇到来电,锁屏,按了Home键等)"); block(SDAuthIDStateSystemCancel, error); }); } break; case LAErrorPasscodeNotSet:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID 无法启动,因为用户没有设置密码"); block(SDAuthIDStatePasswordNotSet, error); }); } break; case LAErrorTouchIDNotEnrolled:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID 无法启动,因为用户没有设置TouchID"); block(SDAuthIDStateTouchIDNotSet, error); }); } break; //case :{ case LAErrorTouchIDNotAvailable:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID 无效"); block(SDAuthIDStateTouchIDNotAvailable, error); }); } break; case LAErrorTouchIDLockout:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"TouchID 被锁定(连续多次验证TouchID失败,系统需要用户手动输入密码)"); block(SDAuthIDStateTouchIDLockout, error); }); } break; case LAErrorAppCancel:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"当前软件被挂起并取消了授权 (如App进入了后台等)"); block(SDAuthIDStateAppCancel, error); }); } break; case LAErrorInvalidContext:{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"当前软件被挂起并取消了授权 (LAContext对象无效)"); block(SDAuthIDStateInvalidContext, error); }); } break; default: break; } } } }]; } else { dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"当前设备不支持TouchID/FaceID"); block(SDAuthIDStateNotSupport, error); }); } } /** * 启动TouchID * @param description TouchID/FaceID显示的描述 * @param localizedFallbackTitle 错误描述 * @param block 回调状态的block */ - (void)showAuthTouchIDWithDescription:(NSString *)description localizedFallbackTitle:(NSString *)localizedFallbackTitle block:(SDAuthIDStateBlock)block { [self showAuthIDWithDescription:description localizedFallbackTitle:localizedFallbackTitle block:block]; } /** * 启动TouchID * @param description TouchID/FaceID显示的描述 * @param localizedFallbackTitle 错误描述 * @param block 回调状态的block */ - (void)showAuthFaceIDWithDescription:(NSString *)description localizedFallbackTitle:(NSString *)localizedFallbackTitle block:(SDAuthIDStateBlock)block { [self showAuthIDWithDescription:description localizedFallbackTitle:localizedFallbackTitle block:block]; } /** 判断设备支持哪种认证方式 TouchID & FaceID */ - (SDAuthIDSupportType)canSupportBiometrics { LAContext *context = [[LAContext alloc] init]; NSError *error; if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) { if (error != nil) { return SDAuthIDSupportTypeNone; } if (@available(iOS 11.0, *)) { return context.biometryType == LABiometryTypeTouchID ? SDAuthIDSupportTypeTouchID:SDAuthIDSupportTypeFaceID ; } } return SDAuthIDSupportTypeNone; } @end
具体使用方式如下;
需要在plist文件中添加 Privacy-Face ID Usage Description 属性 <key>NSFaceIDUsageDescription</key> <string>允许设备访问Face ID</string> 使用方式 判断支持的类型 SDAuthIDSupportType supportType = [[SDAuthID sharedInstance] canSupportBiometrics]; SDAuthIDSupportType supportType = [[SDAuthID sharedInstance] canSupportBiometrics]; NSString *description = @""; if(SDAuthIDSupportTypeFaceID == supportType){ description = @"验证已有面容"; } else if(SDAuthIDSupportTypeTouchID == supportType){ description = @"通过Home键验证已有指纹"; } else { description = @"不支持TouchID/FaceID"; } [[SDAuthID sharedInstance] showAuthIDWithDescription:description localizedFallbackTitle:@"请输入密码" block:^(SDAuthIDState state, NSError *error) { if (state == SDAuthIDStateNotSupport) { // 不支持TouchID/FaceID NSLog(@"对不起,当前设备不支持指纹/面容ID"); } else if(state == SDAuthIDStateFail) { // 认证失败 NSLog(@"指纹/面容ID不正确,认证失败"); } else if(state == SDAuthIDStateTouchIDLockout) { // 多次错误,已被锁定 NSLog(@"多次错误,指纹/面容ID已被锁定,请到手机解锁界面输入密码"); } else if (state == SDAuthIDStateSuccess) { // TouchID/FaceID验证成功 NSLog(@"认证成功!"); } }];
创建flutter plugin,我使用的工具是Android studio。
配置如下内容:
如图所示
flutter 调用原生的代码,需要用到channel,这里使用Method channel
实现调用的platform_interface
import ‘flutter_authid_platform_interface.dart’;
/// An implementation of [FlutterAuthidPlatform] that uses method channels. class MethodChannelFlutterAuthid extends FlutterAuthidPlatform { /// The method channel used to interact with the native platform. @visibleForTesting final methodChannel = const MethodChannel('flutter_authid'); @override Future<String?> getPlatformVersion() async { final version = await methodChannel.invokeMethod<String>('getPlatformVersion'); return version; } @override Future<Map<dynamic, dynamic>?> getAuthIDSupportType() async { final result = await methodChannel.invokeMethod<Map<dynamic, dynamic>>('getAuthIDSupportType'); return result; } @override Future<Map<dynamic, dynamic>?> authVerification({Map<dynamic, dynamic>? arguments}) async { final result = await methodChannel.invokeMethod<Map<dynamic, dynamic>>('authVerification', arguments); return result; } }
调用的platform_interface的具体的类
import ‘flutter_authid_platform_interface.dart’;
class FlutterAuthid { Future<String?> getPlatformVersion() { return FlutterAuthidPlatform.instance.getPlatformVersion(); } Future<Map<dynamic, dynamic>?> getAuthIDSupportType() { return FlutterAuthidPlatform.instance.getAuthIDSupportType(); } ///Map<dynamic, dynamic> arguments = {}; // arguments['faceAuthDesc'] = "验证已有面容"; // arguments['touchAuthDesc'] = "通过Home键验证已有指纹"; // arguments['noSupportAuthDesc'] = "不支持TouchID/FaceID"; // arguments['fallbackTitle'] = "请输入密码"; Future<Map<dynamic, dynamic>?> authVerification({Map<dynamic, dynamic>? arguments}) { return FlutterAuthidPlatform.instance.authVerification(arguments: arguments); } } /** 注意:<key>NSFaceIDUsageDescription</key> <string>允许设备访问Face ID</string> */
在flutter插件的example中实现插件的调用,具体代码如下
class AuthPage extends StatefulWidget { const AuthPage({Key? key}) : super(key: key); @override State<AuthPage> createState() => _AuthPageState(); } class _AuthPageState extends State<AuthPage> { final _flutterAuthidPlugin = FlutterAuthid(); String authName = ""; String authImage = ""; String hintDescription = ""; String supportType = ""; @override void initState() { // TODO: implement initState super.initState(); setupAuth(); } Future<void> setupAuth() async { Map<dynamic, dynamic> result; // Platform messages may fail, so we use a try/catch PlatformException. // We also handle the message potentially returning null. try { result = await _flutterAuthidPlugin.getAuthIDSupportType() ?? {}; } on PlatformException { result = {}; } print("getAuthIDSupportType:$result"); // If the widget was removed from the tree while the asynchronous platform // message was in flight, we want to discard the reply rather than calling // setState to update our non-existent appearance. if (!mounted) return; setState(() { String type = result['supportType'].toString(); supportType = type; if ("2" == type) { authImage = "auth_face@2x.png"; authName = "面容"; } else if ("1" == type) { authImage = "auth_finger@2x.png"; authName = "指纹"; } hintDescription = result['desc']; }); authVerification(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Auth app'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Image( image: AssetImage("assets/images/$authImage"), width: 100.0, height: 100.0, ), SizedBox(height: 50.0,), Text( 'SDAuthApp\n $hintDescription以进行登录', textAlign: TextAlign.center, style: TextStyle( fontSize: 18, color: Colors.black, ), ), SizedBox(height: 50.0,), TextButton( onPressed: () { authVerification(); }, child: Text( '点击验证$authName', style: TextStyle( fontSize: 17, color: Colors.black, ), ), ) ], ), ), ); } // 点击唤起指纹、面容ID验证 Future<void> authVerification() async { Map<dynamic, dynamic> arguments = {}; arguments['faceAuthDesc'] = "验证已有面容"; arguments['touchAuthDesc'] = "通过Home键验证已有指纹"; arguments['noSupportAuthDesc'] = "不支持TouchID/FaceID"; arguments['fallbackTitle'] = "请输入密码"; Map<dynamic, dynamic> result; // Platform messages may fail, so we use a try/catch PlatformException. // We also handle the message potentially returning null. try { result = await _flutterAuthidPlugin.authVerification(arguments: arguments) ?? {}; } on PlatformException { result = {}; } print("authVerification:$result"); } }
flutter开发实战-指纹、面容ID验证插件实现,主要iOS开发中的指纹、面容ID验证功能,通过flutter的插件实现调用原生的插件。
学习记录,每天不停进步。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。