Android 指纹验证

Android 指纹验证

本篇记录一下在Android上做指纹校验的过程。在某些敏感场景,可以通过指纹验证操作者是否是设备主人。

本篇使用的androidx 下的Biometric来实现的,FingerprintManagerCompat已经被官方标记过时,就不过多描述了。

请添加图片描述

加入依赖

implementation 'androidx.biometric:biometric:1.1.0'

检查是否支持指纹验证

检查设备硬件是否支持或者是否设置了指纹(至少一个或更多)

BiometricManager.BIOMETRIC_SUCCESS == BiometricManager.from(context).canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)

进行验证

核心类BiometricPrompt,通过authenticate方法启动验证。

  1. 创建一个BiometricPrompt
 mBiometricPrompt = BiometricPrompt(`activity or fragment`, `BiometricPrompt.AuthenticationCallback`)
  1. 进行验证
mBiometricPrompt?.authenticate(`BiometricPrompt.PromptInfo`)

上面伪代码中BiometricPrompt的第一个参数,为Activity或Fragment,第二个参数为识别验证的回调,代码如下:

/**
 * A collection of methods that may be invoked by {@link BiometricPrompt} during authentication.
 */
public abstract static class AuthenticationCallback {
    /**
     * Called when an unrecoverable error has been encountered and authentication has stopped.
     *
     * <p>After this method is called, no further events will be sent for the current
     * authentication session.
     *
     * @param errorCode An integer ID associated with the error.
     * @param errString A human-readable string that describes the error.
     */
    public void onAuthenticationError(@AuthenticationError int errorCode, @NonNull CharSequence errString) {}

    /**
     * Called when a biometric (e.g. fingerprint, face, etc.) is recognized, indicating that the
     * user has successfully authenticated.
     *
     * <p>After this method is called, no further events will be sent for the current
     * authentication session.
     *
     * @param result An object containing authentication-related data.
     */
    public void onAuthenticationSucceeded(@NonNull AuthenticationResult result) {}

    /**
     * Called when a biometric (e.g. fingerprint, face, etc.) is presented but not recognized as
     * belonging to the user.
     */
    public void onAuthenticationFailed() {}
}
  • onAuthenticationError:指纹识别异常回调,包含几个常用错误码,详见:
  • onAuthenticationSucceeded:指纹识别通过回调
  • onAuthenticationFailed:指纹识别不通过回调

onAuthenticationError 错误码

/**
 * An error code that may be returned during authentication.
 */
@IntDef({
    ERROR_HW_UNAVAILABLE,
    ERROR_UNABLE_TO_PROCESS,
    ERROR_TIMEOUT,
    ERROR_NO_SPACE,
    ERROR_CANCELED,
    ERROR_LOCKOUT,
    ERROR_VENDOR,
    ERROR_LOCKOUT_PERMANENT,
    ERROR_USER_CANCELED,
    ERROR_NO_BIOMETRICS,
    ERROR_HW_NOT_PRESENT,
    ERROR_NEGATIVE_BUTTON,
    ERROR_NO_DEVICE_CREDENTIAL
})
@Retention(RetentionPolicy.SOURCE)
@interface AuthenticationError {}

上面伪代码中authenticate方法的参数,为设置验证指纹弹窗的基础参数,代码大概长这样:

val promptInfo: BiometricPrompt.PromptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("这里设置Title")
    .setSubtitle("这里设置Subtitle")
    .setDescription("这里设置Description")
    // .setDeviceCredentialAllowed(false)
    .setNegativeButtonText("这里设置关闭按钮文案")
    // .setAllowedAuthenticators()
    .setConfirmationRequired(true)
    .build()

流程很简单,下面提供一个工具类,可以直接拿去用:

BiometricUtils.kt

package com.kongqw.fingerprintdemo

import android.content.Context
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity

class BiometricUtils {

    private val mBuilder: BiometricPrompt.PromptInfo.Builder = BiometricPrompt.PromptInfo.Builder()

    private var mBiometricPrompt: BiometricPrompt? = null

    private var mAuthenticationErrorListener: IAuthenticationErrorListener? = null

    private var mAuthenticationSucceededListener: IAuthenticationSucceededListener? = null

    private var mAuthenticationFailedListener: IAuthenticationFailedListener? = null

    /**
     * 是否支持指纹识别
     */
    fun isSupportBiometric(context: Context): Boolean {
        return try {
            BiometricManager.BIOMETRIC_SUCCESS == BiometricManager.from(context).canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }

    fun setTitle(title: String): BiometricUtils {
        mBuilder.setTitle(title)
        return this
    }

    fun setSubtitle(subtitle: String): BiometricUtils {
        mBuilder.setSubtitle(subtitle)
        return this
    }

    fun setDescription(description: String): BiometricUtils {
        mBuilder.setDescription(description)
        return this
    }

    fun setNegativeButtonText(negativeButtonText: String): BiometricUtils {
        mBuilder.setNegativeButtonText(negativeButtonText)
        return this
    }

    fun setAuthenticationErrorListener(listener: IAuthenticationErrorListener): BiometricUtils {
        mAuthenticationErrorListener = listener
        return this
    }

    fun setAuthenticationSucceededListener(listener: IAuthenticationSucceededListener): BiometricUtils {
        mAuthenticationSucceededListener = listener
        return this
    }

    fun setAuthenticationFailedListener(listener: IAuthenticationFailedListener): BiometricUtils {
        mAuthenticationFailedListener = listener
        return this
    }

    fun authenticate(fragmentActivity: FragmentActivity, succeededListener: IAuthenticationSucceededListener? = null, errorListener: IAuthenticationErrorListener? = null) {
        succeededListener?.apply { mAuthenticationSucceededListener = succeededListener }
        errorListener?.apply { mAuthenticationErrorListener = errorListener }
        mBiometricPrompt = BiometricPrompt(fragmentActivity, /*{ command -> command?.run() },*/ FingerCallBack())
        mBiometricPrompt?.authenticate(mBuilder.build())
    }

    fun authenticate(fragment: Fragment, succeededListener: IAuthenticationSucceededListener? = null, errorListener: IAuthenticationErrorListener? = null) {
        succeededListener?.apply { mAuthenticationSucceededListener = succeededListener }
        errorListener?.apply { mAuthenticationErrorListener = errorListener }
        mBiometricPrompt = BiometricPrompt(fragment, /*{ command -> command?.run() },*/ FingerCallBack())
        mBiometricPrompt?.authenticate(mBuilder.build())
    }

    inner class FingerCallBack : BiometricPrompt.AuthenticationCallback() {

        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
            super.onAuthenticationError(errorCode, errString)
            mAuthenticationErrorListener?.onAuthenticationError(errorCode, errString)
        }

        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            super.onAuthenticationSucceeded(result)
            mBiometricPrompt?.cancelAuthentication()
            mAuthenticationSucceededListener?.onAuthenticationSucceeded(result)
        }

        override fun onAuthenticationFailed() {
            super.onAuthenticationFailed()
            mAuthenticationFailedListener?.onAuthenticationFailed()
        }
    }
    
    interface IAuthenticationErrorListener {
        fun onAuthenticationError(errorCode: Int, errString: CharSequence)
    }

    interface IAuthenticationSucceededListener {
        fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult)
    }

    interface IAuthenticationFailedListener {
        fun onAuthenticationFailed()
    }
}

使用

初始化

private val mBiometricUtils = BiometricUtils()

检查是否支持指纹验证

val isSupportBiometric = mBiometricUtils.isSupportBiometric(applicationContext)

开始验证

mBiometricUtils.setTitle("指纹验证")
    .setSubtitle("需要身份验证")
    .setDescription("Description")
    .setNegativeButtonText("关闭吧")
    .setAuthenticationSucceededListener(object : BiometricUtils.IAuthenticationSucceededListener {
        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            Toast.makeText(applicationContext, "指纹通过", Toast.LENGTH_SHORT).show()
        }
    })
    .setAuthenticationFailedListener(object : BiometricUtils.IAuthenticationFailedListener {
        override fun onAuthenticationFailed() {
            Toast.makeText(applicationContext, "指纹不通过", Toast.LENGTH_SHORT).show()
        }
    })
    .setAuthenticationErrorListener(object : BiometricUtils.IAuthenticationErrorListener {
        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
            // when(errorCode){
            //     BiometricPrompt.ERROR_HW_UNAVAILABLE -> ""
            //     BiometricPrompt.ERROR_UNABLE_TO_PROCESS -> ""
            //     BiometricPrompt.ERROR_TIMEOUT -> ""
            //     BiometricPrompt.ERROR_NO_SPACE -> ""
            //     BiometricPrompt.ERROR_CANCELED -> ""
            //     BiometricPrompt.ERROR_LOCKOUT -> ""
            //     BiometricPrompt.ERROR_VENDOR -> ""
            //     BiometricPrompt.ERROR_LOCKOUT_PERMANENT -> ""
            //     BiometricPrompt.ERROR_USER_CANCELED -> ""
            //     BiometricPrompt.ERROR_NO_BIOMETRICS -> "未注册任何指纹"
            //     BiometricPrompt.ERROR_HW_NOT_PRESENT -> ""
            //     BiometricPrompt.ERROR_NEGATIVE_BUTTON -> ""
            //     BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL -> ""
            // }
            Toast.makeText(applicationContext, "onAuthenticationError($errorCode, $errString)", Toast.LENGTH_SHORT).show()
        }
    })

mBiometricUtils.authenticate(this)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值