• Android 指纹验证


    Android 指纹验证

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

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

    请添加图片描述

    加入依赖

    implementation 'androidx.biometric:biometric:1.1.0'
    
    • 1

    检查是否支持指纹验证

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

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

    进行验证

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

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

    上面伪代码中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() {}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 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 {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

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

    val promptInfo: BiometricPrompt.PromptInfo = BiometricPrompt.PromptInfo.Builder()
        .setTitle("这里设置Title")
        .setSubtitle("这里设置Subtitle")
        .setDescription("这里设置Description")
        // .setDeviceCredentialAllowed(false)
        .setNegativeButtonText("这里设置关闭按钮文案")
        // .setAllowedAuthenticators()
        .setConfirmationRequired(true)
        .build()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    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()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

    使用

    初始化

    private val mBiometricUtils = BiometricUtils()
    
    • 1

    检查是否支持指纹验证

    val isSupportBiometric = mBiometricUtils.isSupportBiometric(applicationContext)
    
    • 1

    开始验证

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
  • 相关阅读:
    保研后,你们都怎么样了?
    【jvm系列-13】jvm性能调优篇---参数设置以及日志分析
    Vue---CSS样式的作用域
    数字经济和法治背景下国企合规数字化转型思考
    搭建卷积神经网络实现训练、预测实战
    ios 代码上下文截屏之后导致的图片异常问题
    Day4.数据可视化拓展图形的绘制
    从实战出发带你搞懂AXI4 (一、AXI4协议解析)
    图的二种遍历-广度优先遍历和深度优先遍历
    层次架构、面向服务架构(四十四)
  • 原文地址:https://blog.csdn.net/q4878802/article/details/125540238