Android - 指纹识别API示例

– 前言

需求说明:app经常要验证码验证用户手机号保证安全性,现在想接入指纹识别来代替验证码验证,不支持指纹识别的设备照常用验证码。

了解API

Android在23(Android M 6.0)新增了对指纹识别的硬件支持,应用可以通过调用系统Api实现指纹验证相关功能,相对于传统的手势,密码等验证方式,指纹验证安全性更高,速度也更快。

  • Android 23(Android M 6.0)新增 指纹识别Api:FingerprintManager,开发者通过该Api打开指纹认证时,系统仅会打开设备的指纹模块监听,并不会有UI相关展示,需要开发者根据自身App要求弹出对应的交互流程。
  • Android 28(Android P 9.0)新增 生物识别Api:BiometricManager,推荐替换掉原来的FM,囊括指纹、人脸、虹膜等生物特征识别,不过现阶段只开放了指纹相关。开发者使用该Api进行指纹认证时,系统在会打开设备的指纹模块监听的同时,还会弹出一个系统级的Dialog提示用户正在进行指纹解锁流程。

Goolge提供的API只是指纹跟设备中的指纹库进行比对,得到验证的状态(成功/失败…),无法得到唯一的指纹信息,也就无法绑定账户。 前端提供一个开关,当用户开启指纹后,将状态保存在本地,但每次指纹识别前都要判断满足要求才能正常走流程。


– 认证流程

第零步:在清单文件中申明权限

<!-- 使用生物特征识别、触摸传感器和指纹认证的许可-->
    <uses-permission android:name="android.permission.USE_BIOMETRIC" />
    <uses-permission android:name="android.permission.USE_FINGERPRINT"/>

第一步:判断设备满足认证的前提要求(四步)

  1. Android6.0及以上版本
  2. 判断硬件支持指纹识别
  3. 判断已设置密码锁
  4. 判断至少注册了一个指纹
/**
* 1.>Android 6.0 & 2.支持指纹识别 & 3.已设置开屏密码 & 4.已录入指纹
*/
private boolean isSupportFingerprint(){
    
    
		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
    
    
            Toast.makeText(mContext, "系统版本过低,不支持指纹功能", Toast.LENGTH_SHORT).show();
            return false;
        } else {
    
    
            KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
            fingerprintManager = (FingerprintManager)getSystemService(FINGERPRINT_SERVICE);
            if (fingerprintManager == null || !fingerprintManager.isHardwareDetected()) {
    
    
                Toast.makeText(mContext, "您的系统不支持指纹功能", Toast.LENGTH_SHORT).show();
                return false;
            } else if (keyguardManager != null && !keyguardManager.isKeyguardSecure()) {
    
    
                Toast.makeText(mContext, "请在设置界面开启锁屏密码,并录入指纹后再尝试", Toast.LENGTH_LONG).show();
                return false;
            } else if (!fingerprintManager.hasEnrolledFingerprints()) {
    
    
            	
                Toast.makeText(mContext, "您还没有录入指纹, 请在系统设置录入至少一个指纹", Toast.LENGTH_LONG).show();
                return false;
            }
            return true;
        }

第二步:两种设备调用不同的指纹管理类

判断版本在9.0以上还是以下,以上用FingerprintManager类进行指纹识别,并自定义监听时的Dialog,以下用BiometricManager类进行指纹识别,自动调用系统Dialog。

1.两种流程

private void selectMode(String num){
    
    
	if(isSupportFingerprint()){
    
    
		//9.0以上
		if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
    
    
	        overallIdentification();
	    }else {
    
    
	    	//6.0~9.0 自定义的Dialog和回调接口
	    	FingerprintDialogFragment dialogFragment = new FingerprintDialogFragment();
	    	dialogFragment.setArguments(bundle);
	    	dialogFragment.showNow(getSupportFragmentManager(),"FingerprintDialogFragment");
	    	dialogFragment.setClickCallBack(new FingerprintDialogFragment.ClickCallBack() {
    
    
	    		@Override
	    		public void clickCancel() {
    
    
	    			dialogFragment.dismiss();
	    		}
	    		@Override
	    		public void clickConfirm() {
    
    
	    			dialogFragment.dismiss();
	    			//...
	    		}
	   		});
		}
    }
}

2. <9.0指纹识别、创建Dialog:FingerprintDialogFragment

1)xml文件

在这里插入图片描述

2)准备密钥:访问Android密钥库并生成用于加密数据的密钥
3)根据密钥生成密码
4)创建指纹认证回调接收事件通知 FingerprintHandler
public class FingerprintDialogFragment extends DialogFragment{
    
    
	//只放关键代码...
    private ClickCallBack clickCallBack;
    private Context mContext;
    public final String KEY_NAME = "fingerprintKey";
    private FingerprintHandler fph;
    private FingerprintManager.CryptoObject cryptoObject;
    private FingerprintManager fingerprintManager;

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    
    
        super.onActivityCreated(savedInstanceState);
        initWindow();
        initView();
        mContext = getContext();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    
    
            fingerprintManager = (FingerprintManager)mContext.getSystemService(FINGERPRINT_SERVICE);
            fph = new FingerprintHandler(tvInfo);
            initStart();
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    private void initStart() {
    
    
        generateKey();
        //开启指纹验证
        fph.doAuth(fingerprintManager, cryptoObject);
    }

    //准备密钥
    @RequiresApi(api = Build.VERSION_CODES.M)
    private void generateKey() {
    
    
        try {
    
    
            //生成用于加密数据的非对称密钥
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            //密钥生成器
            KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
            //初始化密钥生成器
            keyStore.load(null);
            keyGenerator.init(new
                    KeyGenParameterSpec.Builder(KEY_NAME,
                    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT).setBlockModes(KeyProperties.BLOCK_MODE_CBC).setUserAuthenticationRequired(true).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7).build());
            keyGenerator.generateKey();
            Cipher cipher = generateCipher(keyStore);
            cryptoObject = new FingerprintManager.CryptoObject(cipher);
        } catch (NoSuchAlgorithmException| NoSuchPaddingException | InvalidKeyException | UnrecoverableKeyException  | KeyStoreException exc) {
    
    
            exce.printStackTrace();
        }
    }

    // 生成密码
    private Cipher generateCipher(KeyStore keyStore) {
    
    
        try {
    
    
            Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
            SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,null);//算法
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher;
        } catch (NoSuchAlgorithmException| NoSuchPaddingException | InvalidKeyException | UnrecoverableKeyException  | KeyStoreException exc) {
    
    
            exc.printStackTrace();
        }
        return null;
    }

    // 指纹认证回调:接收事件通知,并且可以知道何时身份验证成功或出现问题
    @RequiresApi(api = Build.VERSION_CODES.M)
    class FingerprintHandler extends FingerprintManager.AuthenticationCallback {
    
    
        private TextView tv;

        public FingerprintHandler(TextView tv) {
    
    
            this.tv = tv;
        }

        @Override
        public void onAuthenticationError(int errorCode, CharSequence errString) {
    
    
            super.onAuthenticationError(errorCode, errString);
            if (errorCode == 1) {
    
    
                tv.setTextColor(Color.parseColor("#C8374A"));
                tv.setText(errString + "请录入指纹");
            }else if(errorCode != 5){
    
    
                tv.setTextColor(Color.parseColor("#C8374A"));
                tv.setText(errString);
            }
        }

        @Override
        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
    
    
            super.onAuthenticationHelp(helpCode, helpString);
            LogUtils.d("Fingerprint---helpCode:-"+helpCode+"---helpString:"+helpString);
            tv.setTextColor(Color.parseColor("#C8374A"));
            tv.setText(helpString);

        }

        @Override
        public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
    
    
            super.onAuthenticationSucceeded(result);
            tv.setTextColor(Color.parseColor("#0c0c0c"));//#C8374A 请触摸指纹传感器
            tv.setText("验证成功");
            if(type==1){
    
    
                EventBus.getDefault().post(new FingerprintGoDealBean());
            }else if(type==2){
    
    
                EventBus.getDefault().post(new FingerprintTransferBean());
            }
            FingerprintDialogFragment.this.dismiss();
        }

        @Override
        public void onAuthenticationFailed() {
    
    //失败次数过多会中止响应一段时间而后再中止sensor的工作
            super.onAuthenticationFailed();
            LogUtils.d("Fingerprint---验证失败-");
            tv.setTextColor(Color.parseColor("#C8374A"));//#C8374A 请触摸指纹传感器
            tv.setText("验证失败");
        }

        public void doAuth(FingerprintManager manager, FingerprintManager.CryptoObject obj) {
    
    
            CancellationSignal signal = new CancellationSignal();
            try {
    
    
                manager.authenticate(obj, signal, 0, this, null);
            } catch (SecurityException sce) {
    
    
            }
        }
    }


    private void initView() {
    
    
        tvCancel.setOnClickListener(v -> clickCallBack.clickCancel());
        tvConfirm.setOnClickListener(v -> clickCallBack.clickConfirm());
    }

    interface ClickCallBack {
    
    
        void clickCancel();
        void clickConfirm();
    }

    public void setClickCallBack(ClickCallBack clickCallBack) {
    
    
        this.clickCallBack = clickCallBack;
    }

    private void initWindow() {
    
    
        setCancelable(false);
        Window window = getDialog().getWindow();
        //ColorDrawable(Color.TRANSPARENT)
        window.setBackgroundDrawableResource(R.drawable.shape_body_white_10);
        WindowManager.LayoutParams lp = window.getAttributes();
        lp.width = 800;//280dp
        lp.gravity = Gravity.CENTER;
        window.setAttributes(lp);
    }

}

2. >9.0指纹识别

系统提供的Dialog:(每个厂商不一样)
在这里插入图片描述
在这里插入图片描述

已知的错误状态码errorCode含义:1目前不可用,稍后再试、3超时(与设备和传感器类型有关)、4设备可用存储空间不足、5取消验证、7失败次数太多等待30s、9失败太多次生物验证锁定、10中途取消、13点击了negative button

	@RequiresApi(api = Build.VERSION_CODES.P)
    private void overallIdentification(){
    
    
        BiometricPrompt prompt = new BiometricPrompt.Builder(mContext).setTitle("指纹验证").setSubtitle(" ").setDescription("请触摸指纹传感器")
                .setNegativeButton("使用验证码", command -> {
    
    
                	//点击监听...
                }, (dialog, which) -> {
    
    
                }).build();
        BiometricPrompt.AuthenticationCallback callback = new BiometricPrompt.AuthenticationCallback() {
    
    
            @Override
            public void onAuthenticationFailed() {
    
    
                super.onAuthenticationFailed();
                ToastUtils.showMessage("验证失败");
            }

            @Override
            public void onAuthenticationError(int errorCode, CharSequence errString) {
    
    
                super.onAuthenticationError(errorCode, errString);
                if(errorCode!=10){
    
    
                    ToastUtils.showMessage(errString+"");
                }
            }

            @Override
            public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
    
    
                super.onAuthenticationHelp(helpCode, helpString);
                ToastUtils.showMessage(helpString+"");
            }

            @Override
            public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
    
    
                super.onAuthenticationSucceeded(result);
                //认证成功...
            }
        };
        CancellationSignal signal = new CancellationSignal();
        //唤起指纹认证界面:展示验证对话框,调用基于加密的身份验证。
        prompt.authenticate(signal,mContext.getMainExecutor(),callback);
    }

参考博客:
https://cloud.tencent.com/developer/article/1474404
https://www.jianshu.com/p/74fb11da9a41
https://blog.csdn.net/qq_34676644/article/details/118758483

猜你喜欢

转载自blog.csdn.net/czssltt/article/details/124930710