Flutter设备信任验证插件flutter_trusted_device_v2的使用

发布于 1周前 作者 yuanlaile 来自 Flutter

Flutter设备信任验证插件flutter_trusted_device_v2的使用

这是Fazpass Trusted Device V2的官方Flutter包。

如果您想使用Android原生SDK,可以在这里找到:https://github.com/fazpass-sdk/android-trusted-device-v2

对于iOS版本,您可以在这里找到:https://github.com/fazpass-sdk/ios-trusted-device-v2

您还可以在flutter pub.dev上找到这个包:https://pub.dev/packages/flutter_trusted_device_v2

访问Fazpass官方网站以获取更多产品信息,并查看在线文档以获得更多的技术细节。

最低操作系统要求

  • Android 24及以上版本
  • iOS 13.0及以上版本

安装

在您的项目根目录下运行以下命令:

$ flutter pub add flutter_trusted_device_v2

这将在您的pubspec.yaml文件中添加如下一行(并隐式运行flutter pub get):

dependencies:
  flutter_trusted_device_v2: ^<version>

或者,您可以使用编辑器支持的flutter pub get。请查阅您的编辑器文档了解更多信息。

现在可以在Dart代码中使用:

import 'package:flutter_trusted_device_v2/flutter_trusted_device_v2.dart';

开始使用

在使用此包之前,请务必联系我们以获取公钥和FCM应用ID(仅限iOS)。

该包的主要目的是生成元数据,您可以使用这些元数据与Fazpass REST API通信。但在调用生成元数据方法之前,您需要先通过调用以下方法进行初始化:

Fazpass.instance.init(
    androidAssetName: 'AndroidAssetName.pub',
    iosAssetName: 'iosAssetName',
    iosFcmAppId: 'iosFcmAppId'
);

在Android上开始使用

设置您的公钥

  1. 打开您的Android文件夹,然后进入app/src/main/assets/(如果assets文件夹不存在,请创建一个新的)。
  2. 将公钥放入此文件夹。

然后,打开您的android MainActivity文件(app/src/main/kotlin/<app_package>/MainActivity.kt),并进行一些更改:

// 更改此导入:
// import io.flutter.embedding.android.FlutterActivity
// 更改为:
import io.flutter.embedding.android.FlutterFragmentActivity

// 将MainActivity的父类从FlutterActivity更改为FlutterFragmentActivity
class MainActivity: /*FlutterActivity()*/ FlutterFragmentActivity() {
    // ...
}

之后,打开styles.xml文件(app/src/main/res/values/styles.xml),并将主题更改为AppCompat主题(或其子类):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 当系统暗模式设置关闭时应用于Android窗口的主题 -->
    <!-- 将parent LaunchTheme更改为: -->
    <!-- <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> -->
    <!-- 更改为: -->
    <style name="LaunchTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
        <!-- 显示一个启动屏活动。当Flutter引擎绘制第一帧时自动移除 -->
        <item name="android:windowBackground">@drawable/launch_background</item>
    </style>
    <!-- 应用于Android窗口的主题,一旦进程启动即生效。
         此主题确定您的Flutter UI初始化期间以及运行时背后的Android窗口颜色。

         此主题仅从Flutter的Android嵌入V2开始使用。 -->
    <!-- 将parent NormalTheme更改为: -->
    <!-- <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> -->
    <!-- 更改为: -->
    <style name="NormalTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowBackground">?android:colorBackground</item>
    </style>
</resources>

如果有夜间样式变体(app/src/main/res/values-night/styles.xml),则需要将其更改为暗色AppCompat主题:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 当系统暗模式设置开启时应用于Android窗口的主题 -->
    <style name="LaunchTheme" parent="@style/Theme.AppCompat.DayNight.NoActionBar">
        <!-- 显示一个启动屏活动。当Flutter引擎绘制第一帧时自动移除 -->
        <item name="android:windowBackground">@drawable/launch_background</item>
    </style>
    <!-- 应用于Android窗口的主题,一旦进程启动即生效。
         此主题确定您的Flutter UI初始化期间以及运行时背后的Android窗口颜色。

         此主题仅从Flutter的Android嵌入V2开始使用。 -->
    <style name="NormalTheme" parent="@style/Theme.AppCompat.DayNight.NoActionBar">
        <item name="android:windowBackground">?android:colorBackground</item>
    </style>
</resources>

检索您的应用程序签名

当在Fazpass仪表板中创建新的商户应用时,有一个“签名”输入框。

Fazpass仪表板创建新的商户应用图像

以下是获取签名的方法:

在主widget的initState()方法中添加以下代码行:

[@override](/user/override)
void initState() {
  super.initState();
  
  // 添加这一行
  Fazpass.instance.getAppSignatures().then((value) => print("APPSGN: $value"));
}

然后构建发布版apk。将设备连接到PC并在调试时启动它。打开日志并查询APPSGN。它的值是一个数组,看起来像这样:[Gw+6AWbS7l7JQ7Umb1zcs1aNA8M=]。如果有多个项,请选择其中一个。复制签名Gw+6AWbS7l7JQ7Umb1zcs1aNA8M=并将其填入您的商户应用的签名字段中。

上传您的apk或abb到Play商店后,从Play商店下载您的应用并再次检查应用的签名。如果不同,请确保更新您的商户应用的签名值。

在iOS上开始使用

设置您的公钥

  1. 在XCode项目中,打开Assets。
  2. 添加新的数据集作为资产。
  3. 引用您的公钥到这个资产。
  4. 命名您的资产。

然后,您需要在Info.plist文件中声明NSFaceIDUsageDescription,以便能够生成元数据,因为生成元数据需要用户进行生物识别认证。

然后,在您的XCode项目中的AppDelegate.swift文件中,覆盖didReceiveRemoteNotification函数。

override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  
  // 添加这一行
  Fazpass.shared.getCrossDeviceDataFromNotification(userInfo: userInfo)

  completionHandler(UIBackgroundFetchResult.newData)
}

使用

调用generateMeta()方法以启动本地身份验证(生物识别/密码)并生成元数据。如果本地身份验证成功,则生成元数据;否则抛出BiometricAuthFailedError

String meta = '';
try {
  meta = await Fazpass.instance.generateMeta();
} on FazpassException catch (e) {
  switch (e) {
    case BiometricNoneEnrolledError():
      // TODO
      break;
    case BiometricAuthFailedError():
      // TODO
      break;
    case BiometricUnavailableError():
      // TODO
      break;
    case BiometricUnsupportedError():
      // TODO
      break;
    case EncryptionException():
      // TODO
      break;
    case PublicKeyNotExistException():
      // TODO
      break;
    case UninitializedException():
      // TODO
      break;
    case BiometricSecurityUpdateRequiredError():
      // TODO
      break;
  }
}

异常和错误

UninitializedException

当未调用fazpass的初始化方法时产生。

PublicKeyNotExistException

  • Android: 当在初始化方法中注册的公钥名称在assets目录中不存在时产生。
  • iOS: 当在初始化方法中注册的公钥名称作为asset不存在时产生。

EncryptionException

当由于使用了错误的公钥而导致加密失败时产生。

BiometricAuthError

当生物识别身份验证完成时出现错误时产生。(例如:用户取消了生物识别身份验证,用户多次未能通过生物识别身份验证等)

BiometricUnavailableError

  • Android: 当设备无法启动生物识别身份验证,因为缺少合适的硬件(例如:无生物识别传感器或无锁屏)或硬件不可用时产生。
  • iOS: 当设备无法启动生物识别身份验证,因为生物识别不可用时产生。

BiometricNoneEnrolledError

  • Android: 当设备无法启动生物识别身份验证,因为没有生物识别信息(例如:指纹、面部、虹膜)或设备凭据(例如:PIN、密码、图案)注册时产生。
  • iOS: 当设备无法启动生物识别身份验证,因为没有生物识别(触控ID或面容ID)或设备密码注册时产生。

BiometricUnsupportedError

  • Android: 当设备无法启动生物识别身份验证,因为指定的选项与当前Android版本不兼容时产生。
  • iOS: 当设备无法启动生物识别身份验证,因为显示所需的认证用户界面被禁止时产生。要解决此问题,您需要通过将interactionNotAllowed属性设置为false来允许显示认证UI。

Android独有异常

BiometricSecurityUpdateRequiredError

当设备无法启动生物识别身份验证,因为一个或多个硬件传感器发现了一个安全漏洞。受影响的传感器在解决此问题之前不可用时产生。

设置数据收集偏好

此包支持具有多个帐户的应用程序,每个帐户可以有不同的生成元数据设置。要设置数据收集偏好,请调用setSettings()方法。

// 帐户索引
int accountIndex = 0;

// 创建偏好
FazpassSettings settings = FazpassSettingsBuilder()
  .enableSelectedSensitiveData([SensitiveData.location])
  .setBiometricLevelToHigh()
  .build();

// 保存偏好
await Fazpass.instance.setSettings(accountIndex, settings);

// 使用相同的帐户索引应用已保存的偏好
String meta = await Fazpass.instance.generateMeta(accountIndex: accountIndex);

// 删除已保存的偏好
await Fazpass.instance.setSettings(accountIndex, null);

generateMeta()方法的accountIndex参数默认值为-1

强烈建议不要将偏好保存到默认帐户索引。如果您的应用程序只允许一个活动帐户,请使用0

数据收集

生成的元数据中收集和存储的数据。根据数据收集方式的不同,数据类型分为三类:一般数据、敏感数据和其他数据。

一般数据总是会被收集,而敏感数据需要更复杂的程序才能被收集。其他数据是一种特殊的情况,它们收集复杂的测试结果,可能会影响generateMeta()方法的工作方式。

要启用敏感数据收集,您需要设置偏好并指定您想要收集哪些敏感数据。

FazpassSettingsBuilder builder = FazpassSettingsBuilder()
    .enableSelectedSensitiveData([
      SensitiveData.location,
      SensitiveData.simNumbersAndOperators,
      SensitiveData.vpn
]);

然后,您需要遵循如何启用每种敏感数据的程序,如其各自的段落中所述。

对于其他数据,您也需要设置偏好并指定您想要启用哪些数据。

FazpassSettingsBuilder builder = FazpassSettingsBuilder()
    .setBiometricLevelToHigh();

有关详细信息,请阅读它们各自段落中的描述。

收集的一般数据

  • 您的设备平台名称(Android上的值为"android",iOS上的值为"ios")。
  • 您的应用程序包名(iOS上的捆绑标识符)。
  • 您的应用程序调试状态。
  • 您的设备root状态(iOS上的越狱状态)。
  • 您的设备模拟器/仿真器状态。
  • 您的应用程序克隆状态(仅限Android)。
  • 您的设备镜像或投影状态。
  • 您的应用程序签名(仅限Android)。
  • 您的设备信息(Android/iOS版本、手机品牌/型号、手机类型、手机CPU)。
  • 您的网络IP地址。
  • 您的网络VPN状态(仅限Android)。

收集的敏感数据

您的设备位置和模拟位置状态

可用性:Android、iOS

要在Android上启用位置,请确保请求用户授予以下权限:

  • android.permission.ACCESS_COARSE_LOCATIONandroid.permission.ACCESS_FINE_LOCATION
  • android.permission.FOREGROUND_SERVICE

要在iOS上启用位置,请在您的Info.plist文件中声明NSLocationWhenInUseUsageDescription

您的设备SIM号码和运营商(如有)

可用性:Android

要在Android上启用SIM号码和运营商,请确保请求用户授予以下权限:

  • android.permission.READ_PHONE_NUMBERS
  • android.permission.READ_PHONE_STATE
您的网络VPN状态

可用性:iOS

要在iOS上启用VPN,请在您的Xcode项目中启用Network Extensions功能。

收集的其他数据

高级生物识别

启用高级生物识别会使generateMeta()方法仅使用生物识别,防止用户使用密码作为另一种选择。启用此功能后,立即调用generateNewSecretKey()方法以创建一个将安全地存储在设备密钥库提供程序中的秘密密钥。从此以后,使用高级生物识别偏好调用generateMeta()会使用新创建的秘密密钥进行加密和解密测试。每次测试失败时,意味着秘密密钥已被无效化,因为发生了以下情况之一:

  • 设备注册了新的生物识别信息(新的指纹、面部或虹膜)
  • 设备清除了所有生物识别信息
  • 设备删除了他们的设备密码(密码、PIN、图案等)

当秘密密钥被无效化时,尝试调用Fazpass Check API将会失败。推荐的操作是在启用高级生物识别的每个帐户上注销,并重新使用低级生物识别设置登录。如果您想在秘密密钥被无效化后重新启用高级生物识别,请确保再次调用generateNewSecretKey()

处理传入的跨设备通知

当应用程序处于后台状态(未运行)时,传入的跨设备通知将进入您的系统通知栏并显示为通知。按下所述通知将启动应用程序,并将跨设备数据作为参数传递。当应用程序处于前台状态(当前正在运行)时,传入的跨设备通知将立即发送到应用程序,而不会显示任何通知。

要检索后台状态下的跨设备通知数据,请调用getCrossDeviceDataFromNotification()方法。

CrossDeviceData data = await Fazpass.instance.getCrossDeviceDataFromNotification();

要检索前台状态下的跨设备通知数据,请调用getCrossDeviceDataStreamInstance()方法以获取流实例,然后开始监听流。

// 获取流实例
Stream<CrossDeviceData> crossDeviceStream = Fazpass.instance.getCrossDeviceDataStreamInstance();

// 开始监听流
StreamSubscription<CrossDeviceData> crossDeviceSubs = crossDeviceStream.listen((CrossDeviceData data) {
  // 每次有传入的跨设备通知时都会被调用
  print(data);
  
  if (data.status == "request") {
    String notificationId = data.notificationId!;
    print(notificationId);
  } else if (data.status == "validate") {
    String action = data.action!;
    print(action);
  }
});

// 停止监听流
crossDeviceSubs.cancel();

示例代码

import 'dart:async';

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

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

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

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _meta = 'Not generated yet';
  final _fazpass = Fazpass.instance;

  [@override](/user/override)
  void initState() {
    super.initState();

    _fazpass.getAppSignatures().then((value) => print("APPSGN: $value"));

    initializeFazpass()
      .then((value) => setFazpassSettings())
      .then((value) => generateMeta());
  }

  Future<void> initializeFazpass() async {
    // 初始化fazpass包
    await _fazpass.init(androidAssetName: 'my_public_key.pub');
  }

  Future<void> setFazpassSettings() async {
    // 创建设置
    final settings = FazpassSettingsBuilder()
        .enableSelectedSensitiveData([SensitiveData.simNumbersAndOperators, SensitiveData.location])
        .setBiometricLevelToHigh()
        .build();
    // 生成新的密钥,以便我们可以使用高级生物识别
    await _fazpass.generateNewSecretKey();
    // 保存设置
    await _fazpass.setSettings(1, settings);
  }

  Future<void> generateMeta() async {
    String meta = 'Failed to generate meta.';
    try {
      // 使用保存的设置为帐户索引1生成元数据
      meta = await _fazpass.generateMeta(accountIndex: 1);
    } on FazpassException catch (e) {
      switch (e) {
        case BiometricNoneEnrolledError():
          // TODO
          break;
        case BiometricAuthFailedError():
          // TODO
          break;
        case BiometricUnavailableError():
          // TODO
          break;
        case BiometricUnsupportedError():
          // TODO
          break;
        case EncryptionException():
          // TODO
          break;
        case PublicKeyNotExistException():
          // TODO
          break;
        case UninitializedException():
          // TODO
          break;
        case BiometricSecurityUpdateRequiredError():
          // TODO
          break;
      }
    }

    if (!mounted) return;

    setState(() {
      _meta = meta;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Text(_meta),
        ),
      ),
    );
  }
}

更多关于Flutter设备信任验证插件flutter_trusted_device_v2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter设备信任验证插件flutter_trusted_device_v2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用Flutter设备信任验证插件flutter_trusted_device_v2的示例代码案例。这个插件通常用于验证设备是否可信,例如通过设备指纹、硬件标识符等方式来确认设备的唯一性和安全性。

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

dependencies:
  flutter:
    sdk: flutter
  flutter_trusted_device_v2: ^最新版本号  # 请替换为实际可用的最新版本号

然后运行flutter pub get来安装插件。

接下来,你可以在Flutter项目中编写代码来使用这个插件。以下是一个简单的示例,展示了如何进行设备信任验证:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Device Trust Verification'),
        ),
        body: Center(
          child: DeviceTrustVerificationExample(),
        ),
      ),
    );
  }
}

class DeviceTrustVerificationExample extends StatefulWidget {
  @override
  _DeviceTrustVerificationExampleState createState() => _DeviceTrustVerificationExampleState();
}

class _DeviceTrustVerificationExampleState extends State<DeviceTrustVerificationExample> {
  String deviceTrustStatus = 'Checking...';

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

  Future<void> _checkDeviceTrust() async {
    try {
      // 获取设备信任状态
      bool isTrusted = await FlutterTrustedDeviceV2.isTrusted();

      // 更新UI状态
      setState(() {
        deviceTrustStatus = isTrusted ? 'Device is Trusted' : 'Device is NOT Trusted';
      });
    } catch (e) {
      // 处理异常
      setState(() {
        deviceTrustStatus = 'Error: ${e.message}';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text(
          deviceTrustStatus,
          style: TextStyle(fontSize: 20),
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: () {
            // 这里可以添加重新验证设备信任状态的逻辑,如果需要的话
            _checkDeviceTrust();
          },
          child: Text('Re-check Device Trust'),
        ),
      ],
    );
  }
}

在这个示例中,我们做了以下几件事情:

  1. pubspec.yaml文件中添加了flutter_trusted_device_v2插件的依赖。
  2. 创建了一个简单的Flutter应用,包含一个按钮和一个显示设备信任状态的文本。
  3. _DeviceTrustVerificationExampleState类的initState方法中,调用FlutterTrustedDeviceV2.isTrusted()方法来检查设备是否可信,并更新UI状态。
  4. 在UI中显示设备信任状态,并提供一个按钮来重新检查设备信任状态(如果需要的话)。

请注意,flutter_trusted_device_v2插件的具体实现和API可能会有所不同,因此你需要参考插件的官方文档和示例代码来确保正确使用该插件。此外,设备信任验证通常涉及敏感信息和安全策略,因此在实际应用中需要谨慎处理。

回到顶部