好久沒寫文章了,最近也比較偷懶澎埠,今天繼續(xù)討論我實(shí)際開發(fā)中遇到的需求,那就是關(guān)于APP解鎖始藕,大家都知道∑盐龋現(xiàn)在越來越多的APP在填入賬號密碼后,第二次登錄后伍派,基本不會再次重復(fù)輸入賬號密碼了江耀。而是快捷登錄,而常用的就是** 指紋解鎖** 和 手勢解鎖 二種.
好了诉植,我們就開始我們今天的解鎖之旅。
這邊我只是展示我的需求的邏輯,不同項(xiàng)目可能邏輯不同舌稀,不影響本文主要內(nèi)容啊犬。
主要步驟就分三步:
- 賬號密碼登錄。登錄成功后彈出一個(gè)彈框讓用戶選擇快捷登錄方式壁查。
- 然后跳到相應(yīng)的快捷登錄的設(shè)置界面
- 下次登錄的時(shí)候就進(jìn)行快捷登錄
我們一步步來看觉至。
快捷登錄方式選擇
當(dāng)用賬號密碼登錄成功后,我們就在登錄界面直接彈出一個(gè)彈框睡腿,然后讓用戶選擇想要的快捷登錄方式语御,當(dāng)然如果用戶二種都不想要,那就直接按取消嫉到,然后登錄到主頁沃暗,然后下次再打開應(yīng)用就會又要重新輸入賬號密碼月洛。
這里就會遇到我們的第一個(gè)問題:
因?yàn)锳ndroid手機(jī)有很多種類何恶,有些有指紋,有些沒有指紋嚼黔, 那我們需要在有指紋的時(shí)候细层,跳出這個(gè)有二種選擇的彈框,如果沒有指紋解鎖唬涧,就直接跳到手勢解鎖的界面疫赎。
我的判斷可能比較籠統(tǒng),當(dāng)然還有更好的:
- 我直接就判斷SDK是否>= 23碎节,因?yàn)橹讣y解鎖是SDK 23 出來的捧搞,但是很多國產(chǎn)手機(jī)可能是Android 5的系統(tǒng),但是也有指紋解鎖狮荔。這里我就直接忽略了胎撇。莫怪我心狠。
- 在網(wǎng)上看到有人用反射殖氏,就是在Application中晚树,用反射獲取FingerprintManager這個(gè)類的對象,看是否能成功獲取雅采,如果能爵憎,就存一個(gè)boolean變量為ture,說明這個(gè)手機(jī)里面有指紋相關(guān)的婚瓜。如果獲取失敗宝鼓,就說明沒有指紋。
public class MyApplication extends Application {
public static final String HAS_FINGERPRINT_API = "hasFingerPrintApi";
public static final String SETTINGS = "settings";
@Override
public void onCreate() {
super.onCreate();
SharedPreferences sp = getSharedPreferences(SETTINGS, MODE_PRIVATE);
if (sp.contains(HAS_FINGERPRINT_API)) { // 檢查是否存在該值巴刻,不必每次都通過反射來檢查
return;
}
SharedPreferences.Editor editor = sp.edit();
try {
Class.forName("android.hardware.fingerprint.FingerprintManager"); // 通過反射判斷是否存在該類
editor.putBoolean(HAS_FINGERPRINT_API, true);
} catch (ClassNotFoundException e) {
editor.putBoolean(HAS_FINGERPRINT_API, false);
e.printStackTrace();
}
editor.apply();
}
}
我們解決了彈出框彈出的時(shí)機(jī)后席函,我們就要來做這個(gè)彈出框:
我以前做彈出框都是使用Dialog系列,后來無意間看到谷歌推薦大家使用DialogFragment來做彈框冈涧,取代原來的Dialog茂附,所以正好借著這次機(jī)會正蛙,自己寫了這個(gè)DialogFragment。我下面只給出重要部分营曼。具體的大家去百度下DialogFragment即可乒验。
public class LockChooseFragment extends DialogFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
//設(shè)置DialogFragment 的主題及彈框的Style。
setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme_Material_Light_Dialog);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_lock_choose, container, false);
unbinder = ButterKnife.bind(this, view);
return view;
}
@Override
public void onResume() {
super.onResume();
aty = (LoginActivity) getActivity();
//讓我們的彈框無法點(diǎn)擊外面區(qū)域消失
getDialog().setCanceledOnTouchOutside(false);
getDialog().setCancelable(false);
}
}
好了蒂阱。接下去彈框出來了要點(diǎn)擊一種解鎖锻全,然后進(jìn)行下一個(gè)界面。我們先從簡單的手勢解鎖來說好了录煤。
手勢解鎖
我用的是Github的開源手勢解鎖:PatternLockView
哈哈鳄厌,是不是太簡單了。妈踊。了嚎。莫怪我偷懶啊。因?yàn)間ithub中的API寫的很清楚了廊营。我就不重復(fù)介紹怎么使用歪泳。我使用了覺得的確還不錯(cuò)。推薦哈露筒。
指紋解鎖
首先我們知道谷歌提供了fingerprint包呐伞。包下面的類具體有下面這些:
- FingerprintManager:主要用來協(xié)調(diào)管理和訪問指紋識別硬件設(shè)備
- FingerprintManager.AuthenticationCallback這個(gè)一個(gè)callback接口,當(dāng)指紋認(rèn)證后系統(tǒng)會回調(diào)這個(gè)接口通知app認(rèn)證的結(jié)果是什么
- FingerprintManager.AuthenticationResult這是一個(gè)表示認(rèn)證結(jié)果的類慎式,會在回調(diào)接口中以參數(shù)給出
- FingerprintManager.CryptoObject這是一個(gè)加密的對象類伶氢,用來保證認(rèn)證的安全性。
在開始之前瘪吏,我們需要知道使用指紋識別硬件的基本步驟:
在AndroidManifest.xml中申明如下權(quán)限:
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
獲得FingerprintManager的對象引用
在運(yùn)行是檢查設(shè)備指紋識別的兼容性癣防,比如是否有指紋識別設(shè)備等。
下面我們詳細(xì)說一下上面的2和3 步驟:
獲得FingerprintManager的對象引用
這是app開發(fā)中獲得系統(tǒng)服務(wù)對象的常用方式肪虎,如下:
// Using the Android Support Library v4
fingerprintManager = FingerprintManagerCompat.from(this);
// Using API level 23:
fingerprintManager = (FingerprintManager)getSystemService(Context.FINGERPRINT_SERVICE);
上面給出兩種方式劣砍,第一種是通過V4支持包獲得兼容的對象引用,這是google推行的做法扇救;還有就是直接使用api 23 framework中的接口獲得對象引用刑枝。
在運(yùn)行是檢查設(shè)備指紋識別的兼容性,比如是否有指紋識別設(shè)備等
檢查運(yùn)行條件要使得我們的指紋識別app能夠正常運(yùn)行迅腔,有一些條件是必須滿足的装畅。
- API level 23
指紋識別API是在api level 23也就是android 6.0中加入的,因此我們的app必須運(yùn)行在這個(gè)系統(tǒng)版本之上沧烈。因此google推薦使用 Android Support Library v4包來獲得FingerprintManagerCompat對象掠兄,因?yàn)樵讷@得的時(shí)候這個(gè)包會檢查當(dāng)前系統(tǒng)平臺的版本。 - 硬件
指紋識別肯定要求你的設(shè)備上有指紋識別的硬件,因此在運(yùn)行時(shí)需要檢查系統(tǒng)當(dāng)中是不是有指紋識別的硬件:
使用 fingerprintManager.isHardwareDetected()
來判斷是否有該硬件支持蚂夕,fingerprintManager.hasEnrolledFingerprints()
判斷是否手機(jī)中錄有指紋迅诬。
這里我在使用我的手機(jī)做開發(fā)時(shí)候就遇到了一個(gè)大坑,上面提到了婿牍。谷歌推薦使用FingerprintManagerCompat侈贷,但是我在用FingerprintManagerCompat 來調(diào)用isHardwareDetected()和hasEnrolledFingerprints()時(shí)候,返回的都是false,但是用FingerprintManager來調(diào)用isHardwareDetected()和hasEnrolledFingerprints()時(shí)候等脂,卻是返回true俏蛮,而實(shí)際上我用的是小米5,Android 6 上遥,API23 的手機(jī)搏屑,也的確是有指紋功能的,所以我不知道為什么反而FingerprintManagerCompat這個(gè)兼容類返回是有問題的,應(yīng)該跟國內(nèi)廠商的底層源碼修改有關(guān)粉楚。我在Google Issue Tracker中也有很多人遇到了這個(gè)問題辣恋。但基本都什么華為,小米解幼,三星等抑党,都不是谷歌親兒子包警。所以后來我用的是FingerprintManager這個(gè)類撵摆,這個(gè)類的使用要求在API23及以上,因?yàn)楫吘构雀璧闹讣y是API23才出來的害晦,而我上面又正好直接判斷API23才顯示指紋解鎖的選項(xiàng)特铝。不謀而合。壹瘟。哈哈鲫剿。可能這里有點(diǎn)偷懶了稻轨。
判斷了是否有硬件支持灵莲,和手機(jī)是否有指紋之后,要注意殴俱,谷歌還需要判斷當(dāng)前設(shè)備必須是處于安全保護(hù)中的政冻,即:你的設(shè)備必須是使用屏幕鎖保護(hù)的,這個(gè)屏幕鎖可以是password线欲,PIN或者圖案都行明场。為什么是這樣呢?因?yàn)間oogle原生的邏輯就是:想要使用指紋識別的話李丰,必須首先使能屏幕鎖才行苦锨,這個(gè)和android 5.0中的smart lock邏輯是一樣的,這是因?yàn)間oogle認(rèn)為目前的指紋識別技術(shù)還是有不足之處,安全性還是不能和傳統(tǒng)的方式比較的舟舒。
KeyguardManager keyguardManager =(KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);
if (keyguardManager.isKeyguardSecure()) {
// this device is secure.
}
所以這里總結(jié)判斷是:
- 設(shè)備是否有硬件支持
- 手機(jī)是否處于安全保護(hù)中(沒開就提示用戶開啟鎖屏功能)
- 手機(jī)中是否有指紋記錄(沒有就提示用戶去設(shè)置應(yīng)用中添加一個(gè)指紋)
好了拉庶,這些前戲都做好了,我們就要開始指紋的驗(yàn)證了秃励。
驗(yàn)證指紋
要開始掃描用戶按下的指紋是很簡單的砍的,只要調(diào)用FingerprintManager的authenticate方法即可,那么現(xiàn)在我們來看一下這個(gè)接口:
上圖是google的api文檔中的描述莺治,現(xiàn)在我們挨個(gè)解釋一下這些參數(shù)都是什么:
- crypto這是一個(gè)加密類的對象鹅搪,指紋掃描器會使用這個(gè)對象來判斷認(rèn)證結(jié)果的合法性。這個(gè)對象可以是null穆律,但是這樣的話桥胞,就意味這app無條件信任認(rèn)證的結(jié)果,雖然從理論上這個(gè)過程可能被攻擊榄审,數(shù)據(jù)可以被篡改砌们,這是app在這種情況下必須承擔(dān)的風(fēng)險(xiǎn)。因此搁进,建議這個(gè)參數(shù)不要置為null浪感。這個(gè)類的實(shí)例化有點(diǎn)麻煩,主要使用javax的security接口實(shí)現(xiàn)饼问。
- cancel 這個(gè)是CancellationSignal類的一個(gè)對象影兽,這個(gè)對象用來在指紋識別器掃描用戶指紋的是時(shí)候取消當(dāng)前的掃描操作,如果不取消的話莱革,那么指紋掃描器會移植掃描直到超時(shí)(一般為30s峻堰,取決于具體的廠商實(shí)現(xiàn)),這樣的話就會比較耗電盅视。建議這個(gè)參數(shù)不要置為null捐名。
- flags 標(biāo)識位,根據(jù)圖的文檔描述闹击,這個(gè)位暫時(shí)應(yīng)該為0镶蹋,這個(gè)標(biāo)志位應(yīng)該是保留將來使用的。
- callback 這個(gè)是FingerprintManager.AuthenticationCallback類的對象赏半,這個(gè)是這個(gè)接口中除了第一個(gè)參數(shù)之外最重要的參數(shù)了贺归。當(dāng)系統(tǒng)完成了指紋認(rèn)證過程(失敗或者成功都會)后,會回調(diào)這個(gè)對象中的接口除破,通知app認(rèn)證的結(jié)果。這個(gè)參數(shù)不能為NULL瑰枫。
- handler 這是Handler類的對象踱葛,如果這個(gè)參數(shù)不為null的話丹莲,那么FingerprintManager將會使用這個(gè)handler中的looper來處理來自指紋識別硬件的消息。通常來講尸诽,開發(fā)這不用提供這個(gè)參數(shù)甥材,可以直接置為null,因?yàn)镕ingerprintManager會默認(rèn)使用app的main looper來處理性含。
根據(jù)上面的參數(shù)洲赵,我們一個(gè)個(gè)來具體的分析:
創(chuàng)建CryptoObject類對象
上面我們分析FingerprintManager的authenticate方法的時(shí)候,看到這個(gè)方法的第一個(gè)參數(shù)就是CryptoObject類的對象商蕴,現(xiàn)在我們看一下這個(gè)對象怎么去實(shí)例化叠萍。
我們知道,指紋識別的結(jié)果可靠性是非常重要的绪商,我們肯定不希望認(rèn)證的過程被一個(gè)第三方以某種形式攻擊苛谷,因?yàn)槲覀円胫讣y認(rèn)證的目的就是要提高安全性。但是格郁,從理論角度來說腹殿,指紋認(rèn)證的過程是可能被第三方的中間件惡意攻擊的,常見的攻擊的手段就是攔截和篡改指紋識別器提供的結(jié)果例书。這里我們可以提供CryptoObject對象給authenticate方法來避免這種形式的攻擊锣尉。
FingerprintManager.CryptoObject是基于Java加密API的一個(gè)包裝類,并且被FingerprintManager用來保證認(rèn)證結(jié)果的完整性决采。通常來講自沧,用來加密指紋掃描結(jié)果的機(jī)制就是一個(gè)Javax.Crypto.Cipher對象。Cipher對象本身會使用由應(yīng)用調(diào)用Android keystore的API產(chǎn)生一個(gè)key來實(shí)現(xiàn)上面說道的保護(hù)功能织狐。
為了理解這些類之間是怎么協(xié)同工作的暂幼,這里我給出一個(gè)用于實(shí)例化CryptoObject對象的包裝類代碼筏勒,我們先看下這個(gè)代碼是怎么實(shí)現(xiàn)的移迫,然后再解釋一下為什么是這樣。
public class CryptoObjectHelper
{
// This can be key name you want. Should be unique for the app.
static final String KEY_NAME = "com.createchance.android.sample.fingerprint_authentication_key";
// We always use this keystore on Android.
static final String KEYSTORE_NAME = "AndroidKeyStore";
// Should be no need to change these values.
static final String KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
static final String TRANSFORMATION = KEY_ALGORITHM + "/" +
BLOCK_MODE + "/" +
ENCRYPTION_PADDING;
final KeyStore _keystore;
public CryptoObjectHelper() throws Exception
{
_keystore = KeyStore.getInstance(KEYSTORE_NAME);
_keystore.load(null);
}
public FingerprintManagerCompat.CryptoObject buildCryptoObject() throws Exception
{
Cipher cipher = createCipher(true);
return new FingerprintManagerCompat.CryptoObject(cipher);
}
Cipher createCipher(boolean retry) throws Exception
{
Key key = GetKey();
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
try
{
cipher.init(Cipher.ENCRYPT_MODE | Cipher.DECRYPT_MODE, key);
} catch(KeyPermanentlyInvalidatedException e)
{
_keystore.deleteEntry(KEY_NAME);
if(retry)
{
createCipher(false);
} else
{
throw new Exception("Could not create the cipher for fingerprint authentication.", e);
}
}
return cipher;
}
Key GetKey() throws Exception
{
Key secretKey;
if(!_keystore.isKeyEntry(KEY_NAME))
{
CreateKey();
}
secretKey = _keystore.getKey(KEY_NAME, null);
return secretKey;
}
void CreateKey() throws Exception
{
KeyGenerator keyGen = KeyGenerator.getInstance(KEY_ALGORITHM, KEYSTORE_NAME);
KeyGenParameterSpec keyGenSpec =
new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(ENCRYPTION_PADDING)
.setUserAuthenticationRequired(true)
.build();
keyGen.init(keyGenSpec);
keyGen.generateKey();
}
}
上面的類會針對每個(gè)CryptoObject對象都會新建一個(gè)Cipher對象管行,并且會使用由應(yīng)用生成的key厨埋。這個(gè)key的名字是使用KEY_NAME變量定義的,這個(gè)名字應(yīng)該是保證唯一的捐顷,建議使用域名區(qū)別荡陷。GetKey方法會嘗試使用Android Keystore的API來解析一個(gè)key(名字就是上面我們定義的),如果key不存在的話迅涮,那就調(diào)用CreateKey方法新建一個(gè)key废赞。
cipher變量的實(shí)例化是通過調(diào)用Cipher.getInstance方法獲得的,這個(gè)方法接受一個(gè)transformation參數(shù)叮姑,這個(gè)參數(shù)制定了數(shù)據(jù)怎么加密和解密唉地。然后調(diào)用Cipher.init方法就會使用應(yīng)用的key來完成cipher對象的實(shí)例化工作据悔。
這里需要強(qiáng)調(diào)一點(diǎn),在以下情況下耘沼,android會認(rèn)為當(dāng)前key是無效的:
- 一個(gè)新的指紋image已經(jīng)注冊到系統(tǒng)中
- 當(dāng)前設(shè)備中的曾經(jīng)注冊過的指紋現(xiàn)在不存在了极颓,可能是被全部刪除了
- 用戶關(guān)閉了屏幕鎖功能
- 用戶改變了屏幕鎖的方式
當(dāng)上面的情況發(fā)生的時(shí)候,Cipher.init方法都會拋出KeyPermanentlyInvalidatedException的異常群嗤,上面我的代碼中捕獲了這個(gè)異常菠隆,并且刪除了當(dāng)前無效的key,然后根據(jù)參數(shù)嘗試再次創(chuàng)建狂秘。
上面的代碼中使用了android的KeyGenerator來創(chuàng)建一個(gè)key并且把它存儲在設(shè)備中骇径。KeyGenerator類會創(chuàng)建一個(gè)key,但是需要一些原始數(shù)據(jù)才能創(chuàng)建key者春,這些原始的信息是通過KeyGenParameterSpec類的對象來提供的既峡。KeyGenerator類對象的實(shí)例化是使用它的工廠方法getInstance進(jìn)行的,從上面的代碼中我們可以看到這里使用的AES(Advanced Encryption Standard )加密算法的碧查,AES會將數(shù)據(jù)分成幾個(gè)組运敢,然后針對幾個(gè)組進(jìn)行加密。
接下來忠售,KeyGenParameterSpec的實(shí)例化是使用它的Builder方法传惠,KeyGenParameterSpec.Builder封裝了以下重要的信息: - key的名字
- key必須在加密和解密的時(shí)候是有效的
- 上面代碼中BLOCK_MODE被設(shè)置為Cipher Block Chaining也就是KeyProperties.BLOCK_MODE_CBC,這意味著每一個(gè)被AES切分的數(shù)據(jù)塊都與之前的數(shù)據(jù)塊進(jìn)行了異或運(yùn)算了稻扬,這樣的目的就是為了建立每個(gè)數(shù)據(jù)塊之間的依賴關(guān)系卦方。
- CryptoObjectHelper類使用了PKSC7(Public Key Cryptography Standard #7)的方式去產(chǎn)生用于填充AES數(shù)據(jù)塊的字節(jié),這樣就是要保證每個(gè)數(shù)據(jù)塊的大小是等同的(因?yàn)樾枰惢蛴?jì)算還有方面算法進(jìn)行數(shù)據(jù)處理泰佳,詳細(xì)可以查看AES的算法原理)盼砍。
- setUserAuthenticationRequired(true)調(diào)用意味著在使用key之前用戶的身份需要被認(rèn)證。
每次KeyGenParameterSpec創(chuàng)建的時(shí)候逝她,他都被用來初始化KeyGenerator浇坐,這個(gè)對象會產(chǎn)生存儲在設(shè)備上的key。
怎么使用CryptoObjectHelper呢黔宛?
下面我們看一下怎么使用CryptoObjectHelper這個(gè)類近刘,我們直接看代碼就知道了:
CryptoObjectHelper cryptoObjectHelper = new CryptoObjectHelper();
fingerprintManager.authenticate(cryptoObjectHelper.buildCryptoObject(), 0,cancellationSignal, myAuthCallback, null);
使用是比較簡單的,首先new一個(gè)CryptoObjectHelper對象臀晃,然后調(diào)用buildCryptoObject方法就能得到CryptoObject對象了觉渴。
取消指紋掃描
上面我們提到了取消指紋掃描的操作,這個(gè)操作是很常見的徽惋。這個(gè)時(shí)候可以使用CancellationSignal這個(gè)類的cancel方法實(shí)現(xiàn):
這個(gè)方法專門用于發(fā)送一個(gè)取消的命令給特定的監(jiān)聽器案淋,讓其取消當(dāng)前操作。
因此险绘,app可以在需要的時(shí)候調(diào)用cancel方法來取消指紋掃描操作踢京。
處理用戶的指紋認(rèn)證結(jié)果
前面我們分析authenticate接口的時(shí)候說道回右,調(diào)用這個(gè)接口的時(shí)候必須提供FingerprintManager.AuthenticationCallback類的對象,這個(gè)對象會在指紋認(rèn)證結(jié)束之后系統(tǒng)回調(diào)以通知app認(rèn)證的結(jié)果的漱挚。在android 6.0中翔烁,指紋的掃描和認(rèn)證都是在另外一個(gè)進(jìn)程中完成(指紋系統(tǒng)服務(wù))的,因此底層什么時(shí)候能夠完成認(rèn)證我們app是不能假設(shè)的旨涝。因此蹬屹,我們只能采取異步的操作方式,也就是當(dāng)系統(tǒng)底層完成的時(shí)候主動通知我們白华,通知的方式就是通過回調(diào)我們自己實(shí)現(xiàn)的FingerprintManager.AuthenticationCallback類慨默,這個(gè)類中定義了一些回調(diào)方法以供我們進(jìn)行必要的處理:
這里寫圖片描述
下面我們簡要介紹一下這些接口的含義:
- OnAuthenticationError(int errorCode, ICharSequence errString) 這個(gè)接口會再系統(tǒng)指紋認(rèn)證出現(xiàn)不可恢復(fù)的錯(cuò)誤的時(shí)候才會調(diào)用,并且參數(shù)errorCode就給出了錯(cuò)誤碼弧腥,標(biāo)識了錯(cuò)誤的原因厦取。這個(gè)時(shí)候app能做的只能是提示用戶重新嘗試一遍。
- OnAuthenticationFailed() 這個(gè)接口會在系統(tǒng)指紋認(rèn)證失敗的情況的下才會回調(diào)管搪。注意這里的認(rèn)證失敗和上面的認(rèn)證錯(cuò)誤是不一樣的虾攻,雖然結(jié)果都是不能認(rèn)證。認(rèn)證失敗是指所有的信息都采集完整更鲁,并且沒有任何異常霎箍,但是這個(gè)指紋和之前注冊的指紋是不相符的;但是認(rèn)證錯(cuò)誤是指在采集或者認(rèn)證的過程中出現(xiàn)了錯(cuò)誤澡为,比如指紋傳感器工作異常等漂坏。也就是說認(rèn)證失敗是一個(gè)可以預(yù)期的正常情況,而認(rèn)證錯(cuò)誤是不可預(yù)期的異常情況媒至。
- OnAuthenticationHelp(int helpMsgId, ICharSequence helpString) 上面的認(rèn)證失敗是認(rèn)證過程中的一個(gè)異常情況顶别,我們說那種情況是因?yàn)槌霈F(xiàn)了不可恢復(fù)的錯(cuò)誤,而我們這里的OnAuthenticationHelp方法是出現(xiàn)了可以回復(fù)的異常才會調(diào)用的拒啰。什么是可以恢復(fù)的異常呢驯绎?一個(gè)常見的例子就是:手指移動太快,當(dāng)我們把手指放到傳感器上的時(shí)候图呢,如果我們很快地將手指移走的話条篷,那么指紋傳感器可能只采集了部分的信息,因此認(rèn)證會失敗蛤织。但是這個(gè)錯(cuò)誤是可以恢復(fù)的,因此只要提示用戶再次按下指紋鸿染,并且不要太快移走就可以解決指蚜。
- OnAuthenticationSucceeded(FingerprintManagerCompati.AuthenticationResult result)這個(gè)接口會在認(rèn)證成功之后回調(diào)。我們可以在這個(gè)方法中提示用戶認(rèn)證成功涨椒。這里需要說明一下摊鸡,如果我們上面在調(diào)用authenticate的時(shí)候绽媒,我們的CryptoObject不是null的話,那么我們在這個(gè)方法中可以通過AuthenticationResult來獲得Cypher對象然后調(diào)用它的doFinal方法免猾。doFinal方法會檢查結(jié)果是不是會攔截或者篡改過是辕,如果是的話會拋出一個(gè)異常。當(dāng)我們發(fā)現(xiàn)這些異常的時(shí)候都應(yīng)該將認(rèn)證當(dāng)做是失敗來來處理猎提,為了安全建議大家都這么做获三。
關(guān)于上面的接口還有2點(diǎn)需要補(bǔ)充一下: -
上面我們說道OnAuthenticationError 和 OnAuthenticationHelp方法中會有錯(cuò)誤或者幫助碼以提示為什么認(rèn)證不成功。Android系統(tǒng)定義了幾個(gè)錯(cuò)誤和幫助碼在FingerprintManager類中锨苏,如下:
我們的callback類實(shí)現(xiàn)的時(shí)候最好需要處理這些錯(cuò)誤和幫助碼疙教。
- 當(dāng)指紋掃描器正在工作的時(shí)候,如果我們?nèi)∠敬尾僮鞯脑捝∽猓到y(tǒng)也會回調(diào)OnAuthenticationError方法的贞谓,只是這個(gè)時(shí)候的錯(cuò)誤碼是FingerprintManager.FINGERPRINT_ERROR_CANCELED(值為5),因此app需要區(qū)別對待葵诈。
比如這是我寫的自定義的AuthenticationCallback類
class FingerAuthCallback extends FingerprintManagerCompat.AuthenticationCallback {
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
super.onAuthenticationError(errMsgId, errString);
showError(errString);
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
super.onAuthenticationHelp(helpMsgId, helpString);
showError(helpString);
}
@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
mIcon.setImageResource(R.drawable.ic_fingerprint_success);
mErrorTextView.setTextColor(
ContextCompat.getColor(context, R.color.success_color));
mErrorTextView.setText(
context.getResources().getString(R.string.fingerprint_success));
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
showError(context.getResources().getString(R.string.fingerprint_not_recognized));
}
}
額外補(bǔ)充
指紋解鎖可以用這個(gè)Github上的開源的庫:FingerprintAuthHelper
我使用了裸弦。起碼我測試沒問題。
谷歌的指紋解鎖的Demo:FingerprintDialog (進(jìn)入后點(diǎn)擊右上角的download按鈕作喘,下載demo)
參考文章:
感謝createchance的 Android 6.0指紋識別App開發(fā)demo