在 Xamarin.iOS 中使用触摸 ID 和人脸 ID

iOS 支持两个生物识别身份验证系统:

  1. 触摸 ID 使用“主页”按钮下的指纹传感器。
  2. 人脸 ID 使用前置相机传感器通过面部扫描对用户进行身份验证。

iOS 7 引入了触摸 ID,iOS 11 引入了人脸 ID。

这些身份验证系统依赖于一个名为 Secure Enclave 的硬件安全处理器。 Secure Enclave 负责加密人脸和指纹数据的数学表示形式,并使用此信息对用户进行身份验证。 根据 Apple 的说法,人脸和指纹数据不会离开设备,也不会备份到 iCloud。 应用通过本地身份验证 API 与 Secure Enclave 交互,无法检索人脸或指纹数据或直接访问 Secure Enclave。

在提供对受保护内容的访问权限之前,应用可以使用触摸 ID 和人脸 ID 对用户进行身份验证。

本地身份验证上下文

iOS 上的生物识别身份验证依赖于本地身份验证上下文对象,该对象是 LAContext 类的实例。 LAContext 类允许写入:

  • 检查生物识别硬件的可用性。
  • 评估身份验证策略。
  • 评估访问控制。
  • 自定义并显示身份验证提示。
  • 重复使用或使身份验证状态失效。
  • 管理凭据。

检测可用的身份验证方法

示例项目包括一个由 AuthenticationViewController 支持的 AuthenticationView。 此类替代方法 ViewWillAppear,可检测可用的身份验证方法:

partial class AuthenticationViewController: UIViewController
{
    // ...
    string BiometryType = "";

    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);
        unAuthenticatedLabel.Text = "";

        var context = new LAContext();
        var buttonText = "";

        // Is login with biometrics possible?
        if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out var authError1))
        {
            // has Touch ID or Face ID
            if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
            {
                context.LocalizedReason = "Authorize for access to secrets"; // iOS 11
                BiometryType = context.BiometryType == LABiometryType.TouchId ? "Touch ID" : "Face ID";
                buttonText = $"Login with {BiometryType}";
            }
            // No FaceID before iOS 11
            else
            {
                buttonText = $"Login with Touch ID";
            }
        }

        // Is pin login possible?
        else if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, out var authError2))
        {
            buttonText = $"Login"; // with device PIN
            BiometryType = "Device PIN";
        }

        // Local authentication not possible
        else
        {
            // Application might choose to implement a custom username/password
            buttonText = "Use unsecured";
            BiometryType = "none";
        }
        AuthenticateButton.SetTitle(buttonText, UIControlState.Normal);
    }
}

当 UI 即将向用户显示时,将调用方法 ViewWillAppear。 此方法定义了一个新的 LAContext 实例,并使用方法 CanEvaluatePolicy 来确定是否启用了生物识别身份验证。 如果是这样,它将检查系统版本和 BiometryType 枚举,以确定哪些生物识别选项可用。

如果未启用生物识别身份验证,应用将尝试回退到 PIN 身份验证。 如果生物识别身份验证和 PIN 身份验证均不可用,则设备所有者未启用安全功能,并且无法通过本地身份验证保护内容。

对用户进行身份验证

示例项目中的 AuthenticationViewController 包括方法 AuthenticateMe,该方法负责对用户进行身份验证:

partial class AuthenticationViewController: UIViewController
{
    // ...
    string BiometryType = "";

    partial void AuthenticateMe(UIButton sender)
    {
        var context = new LAContext();
        NSError AuthError;
        var localizedReason = new NSString("To access secrets");

        // Because LocalAuthentication APIs have been extended over time,
        // you must check iOS version before setting some properties
        context.LocalizedFallbackTitle = "Fallback";

        if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
        {
            context.LocalizedCancelTitle = "Cancel";
        }
        if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
        {
            context.LocalizedReason = "Authorize for access to secrets";
            BiometryType = context.BiometryType == LABiometryType.TouchId ? "TouchID" : "FaceID";
        }

        // Check if biometric authentication is possible
        if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out AuthError))
        {
            replyHandler = new LAContextReplyHandler((success, error) =>
            {
                // This affects UI and must be run on the main thread
                this.InvokeOnMainThread(() =>
                {
                    if (success)
                    {
                        PerformSegue("AuthenticationSegue", this);
                    }
                    else
                    {
                        unAuthenticatedLabel.Text = $"{BiometryType} Authentication Failed";
                    }
                });

            });
            context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason, replyHandler);
        }

        // Fall back to PIN authentication
        else if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, out AuthError))
        {
            replyHandler = new LAContextReplyHandler((success, error) =>
            {
                // This affects UI and must be run on the main thread
                this.InvokeOnMainThread(() =>
                {
                    if (success)
                    {
                        PerformSegue("AuthenticationSegue", this);
                    }
                    else
                    {
                        unAuthenticatedLabel.Text = "Device PIN Authentication Failed";
                        AuthenticateButton.Hidden = true;
                    }
                });

            });
            context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, localizedReason, replyHandler);
        }

        // User hasn't configured any authentication: show dialog with options
        else
        {
            unAuthenticatedLabel.Text = "No device auth configured";
            var okCancelAlertController = UIAlertController.Create("No authentication", "This device does't have authentication configured.", UIAlertControllerStyle.Alert);
            okCancelAlertController.AddAction(UIAlertAction.Create("Use unsecured", UIAlertActionStyle.Default, alert => PerformSegue("AuthenticationSegue", this)));
            okCancelAlertController.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, alert => Console.WriteLine("Cancel was clicked")));
            PresentViewController(okCancelAlertController, true, null);
        }
    }
}

调用方法 AuthenticateMe 以响应用户点按“登录”按钮的操作。 实例化新的 LAContext 对象,并检查设备版本以确定在本地身份验证上下文上设置的属性。

调用方法 CanEvaluatePolicy 以检查是否启用了生物识别身份验证,如有可能,请回退到 PIN 身份验证,最后在没有身份验证可用的情况下提供不安全的模式。 如果有可用的身份验证方法,则方法 EvaluatePolicy 用于显示 UI 并完成身份验证过程。

示例项目包含模拟数据和视图,用于在身份验证成功时显示数据。