Flutter本地身份验证插件local_auth的使用

Flutter本地身份验证插件local_auth的使用

local_auth 是一个Flutter插件,用于执行设备上的本地认证。这包括通过指纹或面部识别等生物特征进行认证。

支持平台

平台 版本要求
Android SDK 16+
iOS 12.0+
macOS 10.14+
Windows Windows 10+

使用方法

设备能力检查

要检查设备是否支持本地认证(如果需要生物特征支持),可以调用 canCheckBiometrics 和/或 isDeviceSupported() 方法:

final LocalAuthentication auth = LocalAuthentication();
final bool canAuthenticateWithBiometrics = await auth.canCheckBiometrics;
final bool canAuthenticate =
    canAuthenticateWithBiometrics || await auth.isDeviceSupported();

已注册的生物特征

canCheckBiometrics 只表示硬件支持情况,不表示设备是否有任何已注册的生物特征。要获取已注册的生物特征列表,可以调用 getAvailableBiometrics() 方法:

final List<BiometricType> availableBiometrics = await auth.getAvailableBiometrics();

if (availableBiometrics.isNotEmpty) {
  // Some biometrics are enrolled.
}

if (availableBiometrics.contains(BiometricType.strong) ||
    availableBiometrics.contains(BiometricType.face)) {
  // Specific types of biometrics are available.
}

认证选项

authenticate() 方法在可能的情况下使用生物特征认证,但也允许回退到PIN码、图案或密码。为了仅要求生物特征认证,可以通过传递 AuthenticationOptions 并将 biometricOnly 设置为 true 来实现:

final bool didAuthenticate = await auth.authenticate(
    localizedReason: 'Please authenticate to show account balance',
    options: const AuthenticationOptions(biometricOnly: true));

自定义对话框消息

可以通过传递 AuthMessages 来自定义每个平台的对话框消息:

import 'package:local_auth_android/local_auth_android.dart';
import 'package:local_auth_darwin/local_auth_darwin.dart';

final bool didAuthenticate = await auth.authenticate(
    localizedReason: 'Please authenticate to show account balance',
    authMessages: const <AuthMessages>[
      AndroidAuthMessages(
        signInTitle: 'Oops! Biometric authentication required!',
        cancelButton: 'No thanks',
      ),
      IOSAuthMessages(
        cancelButton: 'No thanks',
      ),
    ]);

异常处理

authenticate 在许多错误情况下会抛出 PlatformException。可以参考 error_codes.dart 中的已知错误代码来处理特定的错误:

try {
  final bool didAuthenticate = await auth.authenticate(
      localizedReason: 'Please authenticate to show account balance',
      options: const AuthenticationOptions(useErrorDialogs: false));
} on PlatformException catch (e) {
  if (e.code == auth_error.notEnrolled) {
    // Handle no hardware.
  } else if (e.code == auth_error.lockedOut ||
      e.code == auth_error.permanentlyLockedOut) {
    // Handle lockout.
  }
}

示例代码

以下是一个完整的示例应用,展示了如何使用 local_auth 插件:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:local_auth/local_auth.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final LocalAuthentication auth = LocalAuthentication();
  _SupportState _supportState = _SupportState.unknown;
  bool? _canCheckBiometrics;
  List<BiometricType>? _availableBiometrics;
  String _authorized = 'Not Authorized';
  bool _isAuthenticating = false;

  @override
  void initState() {
    super.initState();
    auth.isDeviceSupported().then(
          (bool isSupported) => setState(() => _supportState = isSupported
              ? _SupportState.supported
              : _SupportState.unsupported),
        );
  }

  Future<void> _checkBiometrics() async {
    late bool canCheckBiometrics;
    try {
      canCheckBiometrics = await auth.canCheckBiometrics;
    } on PlatformException catch (e) {
      canCheckBiometrics = false;
      print(e);
    }
    if (!mounted) {
      return;
    }

    setState(() {
      _canCheckBiometrics = canCheckBiometrics;
    });
  }

  Future<void> _getAvailableBiometrics() async {
    late List<BiometricType> availableBiometrics;
    try {
      availableBiometrics = await auth.getAvailableBiometrics();
    } on PlatformException catch (e) {
      availableBiometrics = <BiometricType>[];
      print(e);
    }
    if (!mounted) {
      return;
    }

    setState(() {
      _availableBiometrics = availableBiometrics;
    });
  }

  Future<void> _authenticate() async {
    bool authenticated = false;
    try {
      setState(() {
        _isAuthenticating = true;
        _authorized = 'Authenticating';
      });
      authenticated = await auth.authenticate(
        localizedReason: 'Let OS determine authentication method',
        options: const AuthenticationOptions(
          stickyAuth: true,
        ),
      );
      setState(() {
        _isAuthenticating = false;
      });
    } on PlatformException catch (e) {
      print(e);
      setState(() {
        _isAuthenticating = false;
        _authorized = 'Error - ${e.message}';
      });
      return;
    }
    if (!mounted) {
      return;
    }

    setState(
        () => _authorized = authenticated ? 'Authorized' : 'Not Authorized');
  }

  Future<void> _authenticateWithBiometrics() async {
    bool authenticated = false;
    try {
      setState(() {
        _isAuthenticating = true;
        _authorized = 'Authenticating';
      });
      authenticated = await auth.authenticate(
        localizedReason:
            'Scan your fingerprint (or face or whatever) to authenticate',
        options: const AuthenticationOptions(
          stickyAuth: true,
          biometricOnly: true,
        ),
      );
      setState(() {
        _isAuthenticating = false;
        _authorized = 'Authenticating';
      });
    } on PlatformException catch (e) {
      print(e);
      setState(() {
        _isAuthenticating = false;
        _authorized = 'Error - ${e.message}';
      });
      return;
    }
    if (!mounted) {
      return;
    }

    final String message = authenticated ? 'Authorized' : 'Not Authorized';
    setState(() {
      _authorized = message;
    });
  }

  Future<void> _cancelAuthentication() async {
    await auth.stopAuthentication();
    setState(() => _isAuthenticating = false);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: ListView(
          padding: const EdgeInsets.only(top: 30),
          children: <Widget>[
            Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                if (_supportState == _SupportState.unknown)
                  const CircularProgressIndicator()
                else if (_supportState == _SupportState.supported)
                  const Text('This device is supported')
                else
                  const Text('This device is not supported'),
                const Divider(height: 100),
                Text('Can check biometrics: $_canCheckBiometrics\n'),
                ElevatedButton(
                  onPressed: _checkBiometrics,
                  child: const Text('Check biometrics'),
                ),
                const Divider(height: 100),
                Text('Available biometrics: $_availableBiometrics\n'),
                ElevatedButton(
                  onPressed: _getAvailableBiometrics,
                  child: const Text('Get available biometrics'),
                ),
                const Divider(height: 100),
                Text('Current State: $_authorized\n'),
                if (_isAuthenticating)
                  ElevatedButton(
                    onPressed: _cancelAuthentication,
                    child: const Row(
                      mainAxisSize: MainAxisSize.min,
                      children: <Widget>[
                        Text('Cancel Authentication'),
                        Icon(Icons.cancel),
                      ],
                    ),
                  )
                else
                  Column(
                    children: <Widget>[
                      ElevatedButton(
                        onPressed: _authenticate,
                        child: const Row(
                          mainAxisSize: MainAxisSize.min,
                          children: <Widget>[
                            Text('Authenticate'),
                            Icon(Icons.perm_device_information),
                          ],
                        ),
                      ),
                      ElevatedButton(
                        onPressed: _authenticateWithBiometrics,
                        child: Row(
                          mainAxisSize: MainAxisSize.min,
                          children: <Widget>[
                            Text(_isAuthenticating
                                ? 'Cancel'
                                : 'Authenticate: biometrics only'),
                            const Icon(Icons.fingerprint),
                          ],
                        ),
                      ),
                    ],
                  ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

enum _SupportState {
  unknown,
  supported,
  unsupported,
}

平台集成

iOS 集成

如果要使用 Face ID,需要在 Info.plist 文件中添加以下内容:

<key>NSFaceIDUsageDescription</key>
<string>Why is my app authenticating using face id?</string>

Android 集成

活动更改

local_auth 要求使用 FragmentActivity 而不是 Activity。更新你的应用程序:

  • 如果你直接使用 FlutterActivity,请将其更改为 FlutterFragmentActivity
  • 如果你使用自定义活动,请更新 MainActivity.javaMainActivity.kt 继承自 FlutterFragmentActivity

权限

在项目的 AndroidManifest.xml 文件中添加 USE_BIOMETRIC 权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.app">
  <uses-permission android:name="android.permission.USE_BIOMETRIC"/>
</manifest>

兼容性

对于低于 Android Q 的版本,不要调用 getAvailableBiometrics。而是调用 authenticate 并设置 biometricOnly: true

Android 主题

确保 LaunchTheme 的父级是有效的 Theme.AppCompat 主题,以防止 Android 8 及以下版本崩溃。例如,使用 Theme.AppCompat.DayNight 启用浅色/深色模式。

<style name="LaunchTheme" parent="Theme.AppCompat.DayNight">
  ...
</style>

Sticky Auth

你可以将 stickyAuth 选项设置为 true,以便插件在应用程序被系统置于后台时不会返回失败结果。这可能会发生在用户在有机会认证之前接收到电话时。如果 stickyAuth 设置为 false,这将导致插件返回失败结果给 Dart 应用程序。如果设置为 true,插件将在应用程序恢复时重试认证。


更多关于Flutter本地身份验证插件local_auth的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter本地身份验证插件local_auth的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter应用中使用local_auth插件进行本地身份验证(如指纹或面部识别)的示例代码。这个示例将展示如何请求用户进行身份验证并处理结果。

首先,你需要在你的pubspec.yaml文件中添加local_auth依赖:

dependencies:
  flutter:
    sdk: flutter
  local_auth: ^1.0.0  # 请使用最新版本号

然后运行flutter pub get来安装依赖。

接下来,在你的Flutter项目中,你可以按照以下方式使用local_auth插件:

import 'package:flutter/material.dart';
import 'package:local_auth/local_auth.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Local Authentication Example'),
        ),
        body: Center(
          child: LocalAuthExample(),
        ),
      ),
    );
  }
}

class LocalAuthExample extends StatefulWidget {
  @override
  _LocalAuthExampleState createState() => _LocalAuthExampleState();
}

class _LocalAuthExampleState extends State<LocalAuthExample> {
  bool _canCheckBiometrics = false;
  String _authenticatedMessage = '';

  @override
  void initState() {
    super.initState();
    _checkBiometrics();
  }

  Future<void> _checkBiometrics() async {
    bool canCheckBiometrics;
    LocalAuthentication auth = LocalAuthentication();
    try {
      canCheckBiometrics = await auth.canCheckBiometrics;
    } catch (e) {
      print("Error checking biometrics: $e");
      canCheckBiometrics = false;
    }

    if (!mounted) return;

    setState(() {
      _canCheckBiometrics = canCheckBiometrics;
    });
  }

  Future<void> _authenticate() async {
    setState(() {
      _authenticatedMessage = '';
    });

    bool authenticated = false;
    LocalAuthentication auth = LocalAuthentication();
    try {
      authenticated = await auth.authenticateWithBiometrics(
        localizedReason: 'Please authenticate to continue',
        useErrorDialogs: true,
        stickyAuth: true,
      );
    } catch (e) {
      print("Authentication failed: $e");
    }

    if (!mounted) return;

    setState(() {
      _authenticatedMessage = authenticated
          ? 'Authenticated successfully!'
          : 'Authentication failed.';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text('Can check biometrics: $_canCheckBiometrics'),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _canCheckBiometrics ? _authenticate : null,
          child: Text('Authenticate'),
        ),
        SizedBox(height: 20),
        Text(_authenticatedMessage),
      ],
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,该应用具有以下几个功能:

  1. 检查设备是否支持生物识别:在_checkBiometrics方法中,我们使用LocalAuthentication类的canCheckBiometrics属性来检查设备是否支持生物识别功能。

  2. 显示生物识别支持状态:我们根据_canCheckBiometrics的值在界面上显示设备是否支持生物识别。

  3. 进行身份验证:在_authenticate方法中,我们使用authenticateWithBiometrics方法来请求用户进行身份验证。如果身份验证成功,则显示“Authenticated successfully!”消息;如果失败,则显示“Authentication failed.”消息。

  4. 按钮控制:如果设备支持生物识别,则“Authenticate”按钮可以点击;否则,按钮将不可点击。

这个示例应该可以帮助你快速上手local_auth插件的使用。你可以根据实际需求进一步扩展和修改这个示例。

回到顶部