Android 指纹识别(给应用添加指纹解锁)
使用指纹
说明 : 指纹解锁在23 的时候,官方就已经给出了api ,但是由于Android市场复杂,无法形成统一,硬件由不同的厂商开发,导致相同版本的软件系统,搭载的硬件千变万化,导致由的机型不支持指纹识别,但是,这也挡不住指纹识别在接下来的时间中进入Android市场的趋势,因为它相比较输入密码或图案,它更加简单,相比较密码或者图案,它更炫酷 ,本文Demo 使用最新的28 支持的androidx 库中的API及最近火热的kotlin语言完成的
需要知道的
- FingerprintManager : 指纹管理工具类
- FingerprintManager.AuthenticationCallback :使用验证的时候传入该接口,通过该接口进行验证结果回调
- FingerprintManager.CryptoObject: FingerprintManager 支持的分装加密对象的类
以上是28以下API 中使用的类 在Android 28版本中google 宣布使用Androidx 库代替Android库,所以在28版本中Android 推荐使用androidx库中的类 所以在本文中我 使用的是推荐是用的FingerprintManagerCompat 二者的使用的方式基本相似
如何使用指纹
- 开始验证 ,系统默认的每段时间验证指纹次数为5次 次数用完之后自动关闭验证,并且30秒之内不允行在使用验证
验证的方法是authenticate()
/**
*
*@param crypto object associated with the call or null if none required.
* @param flags optional flags; should be 0
* @param cancel an object that can be used to cancel authentication
* @param callback an object to receive authentication events
* @param handler an optional handler for events
**/
@RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
public void authenticate(@Nullable CryptoObject crypto, int flags,
@Nullable CancellationSignal cancel, @NonNull AuthenticationCallback callback,
@Nullable Handler handler) {
if (Build.VERSION.SDK_INT >= 23) {
final FingerprintManager fp = getFingerprintManagerOrNull(mContext);
if (fp != null) {
android.os.CancellationSignal cancellationSignal = cancel != null
? (android.os.CancellationSignal) cancel.getCancellationSignalObject()
: null;
fp.authenticate(
wrapCryptoObject(crypto),
cancellationSignal,
flags,
wrapCallback(callback),
handler);
}
}
}
arg1: 用于通过指纹验证取出AndroidKeyStore中key的值
arg2: 系统建议为0
arg3: 取消指纹验证 手动关闭验证 可以调用该参数的cancel方法
arg4:返回验证结果
arg5: Handler fingerprint 中的
消息都是通过handler来传递的 如果不需要则传null 会自动默认创建一个主线程的handler来传递消息
使用指纹识别的条件
- 添加权限(这个权限不需要在6.0中做处理)
- 判断硬件是否支持
- 是否已经设置了锁屏 并且已经有一个被录入的指纹
- 判断是否至少存在一条指纹信息
通过零碎的知识完成一个Demo
指纹识别通过之后跳转到 指定页面
进入之后首先弹出对话框,进行指纹验证
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/fingerprint" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="验证指纹" />
<TextView
android:id="@+id/fingerprint_error_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:maxLines="1" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginLeft="5dp"
android:layout_marginTop="10dp"
android:layout_marginRight="5dp"
android:background="#696969" />
<TextView
android:id="@+id/fingerprint_cancel_tv"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_gravity="center"
android:gravity="center"
android:text="取消"
android:textSize="16sp" />
</LinearLayout>
使用DialogFragment 完成对话框 新建一个DialogFragment 并且初始化相关的api
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//获取fingerprintManagerCompat对象
fingerprintManagerCompat = FingerprintManagerCompat.from(context!!)
setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog)
}
在界面显示在前台的时候开始扫描
override fun onResume() {
super.onResume()
startListening()
}
@SuppressLint("MissingPermission")
private fun startListening() {
isSelfCancelled = false
mCancellationSignal = CancellationSignal()
fingerprintManagerCompat.authenticate(FingerprintManagerCompat.CryptoObject(mCipher), 0, mCancellationSignal, object : FingerprintManagerCompat.AuthenticationCallback() {
//验证错误
override fun onAuthenticationError(errMsgId: Int, errString: CharSequence?) {
if (!isSelfCancelled) {
errorMsg.text = errString
if (errMsgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) {
Toast.makeText(mActivity, errString, Toast.LENGTH_SHORT).show()
dismiss()
mActivity.finish()
}
}
}
//成功
override fun onAuthenticationSucceeded(result: FingerprintManagerCompat.AuthenticationResult?) {
MainActivity.startActivity(mActivity, true)
}
//错误时提示帮助,比如说指纹错误,我们将显示在界面上 让用户知道情况
override fun onAuthenticationHelp(helpMsgId: Int, helpString: CharSequence?) {
errorMsg.text = helpString
}
//验证失败
override fun onAuthenticationFailed() {
errorMsg.text = "指纹验证失败,请重试"
}
}, null)
}
在不可见的时候停止验证
if (null != mCancellationSignal) {
mCancellationSignal.cancel()
isSelfCancelled = true
}
在MainActivity 中首先判断是否验证成功 是 跳转到目标页 否则的话需要进行验证
在这个过程中我们需要做的就是判断是否支持,判断是否满足指纹验证的条件(条件在上面)
if (intent.getBooleanExtra("isSuccess", false)) {
WelcomeActivity.startActivity(this)
finish()
} else {
//判断是否支持该功能
if (supportFingerprint()) {
initKey() //生成一个对称加密的key
initCipher() //生成一个Cipher对象
}
}
验证条件
if (Build.VERSION.SDK_INT < 23) {
Toast.makeText(this, "系统不支持指纹功能", Toast.LENGTH_SHORT).show()
return false
} else {
val keyguardManager = getSystemService(KeyguardManager::class.java)
val managerCompat = FingerprintManagerCompat.from(this)
if (!managerCompat.isHardwareDetected) {
Toast.makeText(this, "系统不支持指纹功能", Toast.LENGTH_SHORT).show()
return false
} else if (!keyguardManager.isKeyguardSecure) {
Toast.makeText(this, "屏幕未设置锁屏 请先设置锁屏并添加一个指纹", Toast.LENGTH_SHORT).show()
return false
} else if (!managerCompat.hasEnrolledFingerprints()) {
Toast.makeText(this, "至少在系统中添加一个指纹", Toast.LENGTH_SHORT).show()
return false
}
}
必须生成一个加密的key 和一个Cipher对象
//生成Cipher
private fun initCipher() {
val key = keyStore.getKey(DEFAULT_KEY_NAME, null) as SecretKey
val cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
cipher.init(Cipher.ENCRYPT_MODE, key)
showFingerPrintDialog(cipher)
}
//生成一个key
private fun initKey() {
keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
val builder = KeyGenParameterSpec.Builder(DEFAULT_KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
keyGenerator.init(builder.build())
keyGenerator.generateKey()
}
Demo 是kotlin 写的
Demo地址
来源:juejin.cn/post/7210220134601572410