iOS Developer Learning Flutter. Lesson26 Biometric

生物辨识使用local_auth

Today Preview

1. 安装好後第一步首先还是加权限

iOS, 只有Face ID要加

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

Android这边

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

2. 写code分为三部分

都是透过LocalAuthentication()这个实例

判断装置是否支援Biometric

canEvaluatePolicy☘️☘️☘️

  Future<void> _checkBiometrics() async {
    bool canCheckBiometrics;
    canCheckBiometrics = await _localAuth.canCheckBiometrics;

    if (!mounted) return;

    setState(() {
      _canEvaluatePolicy = canCheckBiometrics ? "是" : "否";
    });
  }

判断是否启用Biometric

LABiometryType☘️☘️☘️
注意一下 这边是回传一个阵列 不是某个类型⚠️⚠️⚠️
所以代表Andorid好像有装置可以同时支援(难保以後iPhone不会)

  Future<void> _getAvailableBiometrics() async {
    List<BiometricType> availableBiometrics;
    availableBiometrics = await _localAuth.getAvailableBiometrics();

    if (!mounted) return;

    setState(() {
      if (availableBiometrics.isEmpty) {
        _biometryType = noEnrolledWording;
      } else {
        switch (availableBiometrics.first) {
          case BiometricType.face:
            _biometryType = "点我验证Face ID";
            break;
          case BiometricType.fingerprint:
            _biometryType = "点我验证Touch ID";
            break;
          default:
            _biometryType = noEnrolledWording;
            break;
        }
      }
    });
  }

验证Biometric

evaluatePolicy☘️☘️☘️

  1. 如果是Face ID的话, 第一次验证这边就会要求权限
  2. 看起来只会回传验证通过或拒绝, 不会拿到拒绝原因(LAError)⚠️⚠️⚠️
  3. PlatformException不是拒绝原因, 而是系统相关的例外, 目前有六种: PasscodeNotSet, NotEnrolled, NotAvailable, OtherOperatingSystem, LockedOut and PermanentlyLockedOut
  4. useErrorDialogs
    若为false, 才会出现PlatformException;
    若为ture(预设), 则验证失败会Alert

    点了会进app的设定页, 满方便的
  5. 若useErrorDialogs为true, 且有给iOSAuthStrings的话, 可以客制文案
  6. stickyAuth
    若为ture, 验证中App进背景不会回传失败(例如接到电话), 回到App时继续验证(黏住了)
    若为false(预设), App进背景即失败
  Future<void> _authenticate() async {
    print("验证中");
    bool authenticated = false;

    try {
      authenticated = await _localAuth.authenticateWithBiometrics(
          localizedReason: 'Scan your fingerprint to authenticate',
          stickyAuth: true,
          useErrorDialogs: true,
          iOSAuthStrings: IOSAuthMessages(
              lockOut: "锁",
              goToSettingsButton: "设定",
              goToSettingsDescription: "请设定",
              cancelButton: "算了"
          )
      );
    } on PlatformException catch (e) {
      print("例外");
      print(e);
    }

    if (!mounted) return;

    final result = authenticated ? "验证成功" : "验证失败";
    scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(result)));

  }

3. 其他

3.1

上面第2节中的三个function中可以看到
官方用了个小技巧mounted
来判断await是否回来了

3.2

如果是Android的话
可以透过_localAuth.stopAuthentication()来中断验证

4. 完码

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

class LessonPageLocalAuthentication extends StatefulWidget {
  @override
  _LessonPageLocalAuthenticationState createState() => _LessonPageLocalAuthenticationState();
}

class _LessonPageLocalAuthenticationState extends State<LessonPageLocalAuthentication> {

  final noEnrolledWording = "未启用生物辨识";

  final scaffoldKey = GlobalKey<ScaffoldState>();
  final LocalAuthentication _localAuth = LocalAuthentication();
  String _canEvaluatePolicy = "";
  String _biometryType = "";

  Future<void> _checkBiometrics() async {
    bool canCheckBiometrics;
    canCheckBiometrics = await _localAuth.canCheckBiometrics;

    if (!mounted) return;

    setState(() {
      _canEvaluatePolicy = canCheckBiometrics ? "是" : "否";
    });
  }

  Future<void> _getAvailableBiometrics() async {
    List<BiometricType> availableBiometrics;
    availableBiometrics = await _localAuth.getAvailableBiometrics();

    if (!mounted) return;

    setState(() {
      if (availableBiometrics.isEmpty) {
        _biometryType = noEnrolledWording;
      } else {
        switch (availableBiometrics.first) {
          case BiometricType.face:
            _biometryType = "点我验证Face ID";
            break;
          case BiometricType.fingerprint:
            _biometryType = "点我验证Touch ID";
            break;
          default:
            _biometryType = noEnrolledWording;
            break;
        }
      }
    });
  }

  Future<void> _authenticate() async {
    print("验证中");
    bool authenticated = false;

    try {
      authenticated = await _localAuth.authenticateWithBiometrics(
          localizedReason: 'Scan your fingerprint to authenticate',
          stickyAuth: true,
          useErrorDialogs: true,
          iOSAuthStrings: IOSAuthMessages(
              lockOut: "锁",
              goToSettingsButton: "设定",
              goToSettingsDescription: "请设定",
              cancelButton: "算了"
          )
      );
    } on PlatformException catch (e) {
      print("例外");
      print(e);
    }

    if (!mounted) return;

    final result = authenticated ? "验证成功" : "验证失败";
    scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(result)));

  }

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

  @override
  Widget build(BuildContext context) {

    return Scaffold(
        key: scaffoldKey,
        appBar: AppBar(
          title: Text("Local Authentication"),
        ),
        body: Container(
            alignment: Alignment.center,
            child: Column(
              children: [
                SizedBox(height: 100),
                Text("您的装置是否支援生物辨识:$_canEvaluatePolicy"),
                OutlineButton(
                    child: Text(_biometryType),
                    onPressed: _biometryType == noEnrolledWording ? null : _authenticate
//                    onPressed: _authenticate
                )
              ],
            )
        )
    );
  }
}

本集内容Android版请见:iOS Developer Learning Android. Lesson 21

下集预告:Map

最後提供一下github.com/mark33699/IDLF


<<:  资料分析商业应用与策略管理 #笔记二

>>:  Day27-移动侦测1

[DAY 12] CNN 简介

前言 总算开始了一个跟DL比较有关系的名词啦(?)一直以来科学家总想模仿动物的大脑来做AI结构,所以...

认识CSS(八):CSS BOX模型

CSS box model 盒子模型也称为区块模型,主要是将传统的HTML区块概念再进一步的规范。我...

Ruby、演算法学习心得(二) Big O notation。

TWICE出新MV啦! 转载於:JYP Entertainment 官方YouTube 非本科生直接...

Day 5 安装Prometheus

2021 铁人赛 DAY5 昨天已经简单介绍过Prometheus了,今天要来将他装在我们的丛集里,...

Angular 深入浅出三十天:表单与测试 Day21 - E2E 测试实作 - 被保人表单

大家如果对於昨天的 E2E 测试如果没有什麽问题的话,今天就来为我们的被保人表单撰写 E2E 测试...