iOS 指纹解锁 检测指纹信息变更

通过LAContext evaluatedPolicyDomainState属性可以获取到当前data类型的指纹信息数据,当指纹增加或者删除,该data就会发生变化,通过记录这个TouchIdData与最新的data做对比就能判断指纹信息是否变更,从而定制app功能。

存在的疑问:
  1. TouchIdData可能为空吗?
    官方文档说明:

Discussion
This property returns a value only when the canEvaluatePolicy(:error:) method succeeds for a biometric policy or the evaluatePolicy(:localizedReason:reply:) method is called and a successful biometric authentication is performed. Otherwise, nil is returned.
只有当canEvaluatePolicy方法执行并返回YES或者evaluatePolicy执行并指纹识别通过,这个属性才能有值,否则为空。

  1. TouchIdData能否获取具体的指纹信息?

The returned data is an opaque structure. It can be used to compare with other values returned by this property to determine whether the authorized database has been updated. However, the nature of the change cannot be determined from this data.
返回的数据是一个不透明的结构。它可以用来与此属性返回的其他值进行比较,以确定是否更新了授权数据库。然而,变化的性质不能从这些数据中确定。

  1. 在指纹信息没有修改的时候,不同app获取到的TouchIdData是一样的吗?
    实测不同的app,在指纹没有变化的情况下TouchIdData是不一样的。但这个是不能打包票的,如果苹果修改了这部分的算法,返回一个相同值也是有可能的。
  1. 添加一个新指纹,再删除刚添加的那个指纹,TouchIdData相对一轮操作之前变化了吗?
    实测TouchIdData没有变化,也就是说TouchIdData是面向结果的,而不是面向过程的,只要最终结果指纹集合一样,TouchIdData就一样。

代码实现

    static var IDENTIFY:String? = nil
    static let SERVICE = "TOUCHID_SERVICE"
    static let ACCOUNT_PREFIX = "TOUCHID_PERFIX"
    open class func setCurrentTouchIdDataIdentity(identity:String )
    {
        //设定当前身份用于存储data
        TouchIdManager.IDENTIFY = identity
    }
  
//获取当前时刻的data
  class func currentOriTouchIdData() -> Data?{
        let context = LAContext()
        var error:NSError? = nil;
//先使用canEvaluatePolicy方法进行评估
        if context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error) {
            
            return context.evaluatedPolicyDomainState
        }
        print("errorMsg:" + self.errorMessageForFails(errorCode:(error?.code)! ))
        return nil
    }


//使用keychain保存当前身份的data
open class func setCurrentIdentityTouchIdData()-> Bool
    {
        if self.currentTouchIdDataIdentity() == nil
        {
            return false;
        }
        else
        {
            if self.currentOriTouchIdData() != nil
            {
                //storage by keychain
                SAMKeychain.setPasswordData(self.currentOriTouchIdData()!, forService:SERVICE, account: ACCOUNT_PREFIX + self.currentTouchIdDataIdentity()!)
                return true;
            }
            else
            {
                return false;
            }
        }
    }


//获取当前身份的上一次存储的data,用于对比
 class func currentIdentityTouchIdData()->Data?
    {
        guard (self.currentTouchIdDataIdentity() != nil) else {
            return nil;
        }
        
        return  SAMKeychain.passwordData(forService: TouchIdManager.SERVICE, account: TouchIdManager.ACCOUNT_PREFIX + self.currentTouchIdDataIdentity()!)
    }


//检测以这个身份设置开始到当前时刻指纹信息是否变更
open class func touchIdInfoDidChange()->Bool
    {
        let data = self.currentOriTouchIdData()
        if data == nil && self.isErrorTouchIDLockout() {
            //lock after unlock failed many times,and the fingerprint is not changed.
            return false
        }
        else
        {
            let oldData = self.currentIdentityTouchIdData()
            
            if oldData == nil
            {
                //never set
                return false
            }
            else if oldData == data
            {
                //not change
                return false
            }
            else
            {
                return true
            }
        }
    }


//检测当前是否为biometryLockout状态
    class func isErrorTouchIDLockout()->Bool
    {
        let context = LAContext()
        var error:NSError?
        context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error)
        
        guard error != nil else {
            return false
        }
        
        if error!.code == LAError.biometryLockout.rawValue {
            return true
        }
        else
        {
            return false
        }
    }
指纹识别的两种LAPolicy:
  • deviceOwnerAuthenticationWithBiometrics
    这个类型不能弹出密码解锁界面,但能更精准的反馈用户操作的状态:如指纹识别三次失败等。
  • deviceOwnerAuthentication
    对识别行为的结果做了简化,无法判断具体状态。但能弹出密码解锁界面。
    结合两者可以使指纹解锁做的更友善一点。
  • 最终效果[正常流程]:指纹识别错误三次回调失败->再点击再识别错误两次->弹出密码解锁界面->密码错误5次->锁定1分钟->再输错->锁定五分钟。

代码实现

 open class func showTouchId(title:String,fallbackTitle:String?, fallbackBlock:TouchIdFallBackBlokc?,resultBlock:TouchIdResultBlock?)
    {
        let context = LAContext();
        context.localizedFallbackTitle = fallbackTitle
        var useableError:NSError?
        if context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &useableError) {
            context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: title) { (success, error) in
                DispatchQueue.main.async {
                    if success
                    {
                        if resultBlock != nil
                        {
                            resultBlock!(true,success,error)
                        }
                    }
                    else
                    {
                        guard let error = error else
                        {
                            return;
                        }
                        
                        print("errorMsg:" + self.errorMessageForFails(errorCode: error._code))
                        
                        if error._code == LAError.userFallback.rawValue
                        {
                            if fallbackBlock != nil
                            {
                                fallbackBlock!()
                            }
                        }
                        else if error._code == LAError.biometryLockout.rawValue
                        {
                            //try to show password interface
                            self.tryShowTouchIdOrPwdInterface(title: title, resultBlock: resultBlock)
                        }
                        else
                        {
                            if resultBlock != nil
                            {
                                resultBlock!(true,success,error)
                            }
                        }
                    }
                }
            }
        }
        else
        {
            print("errorMsg:" + self.errorMessageForFails(errorCode:(useableError?.code)! ))
            
            if useableError?.code == LAError.biometryLockout.rawValue
            {
                //try to show password interface
                self.tryShowTouchIdOrPwdInterface(title: title, resultBlock: resultBlock)
            }
            else
            {
                if resultBlock != nil
                {
                    resultBlock!(false,false,useableError)
                }
            }
        }
    }
    
    class func tryShowTouchIdOrPwdInterface(title:String,resultBlock:TouchIdResultBlock?)
    {
        let context = LAContext();
        context.localizedFallbackTitle = ""
        var useableError:NSError?
        if context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error: &useableError) {
            context.evaluatePolicy(LAPolicy.deviceOwnerAuthentication, localizedReason: title) { (success, error) in
                
                DispatchQueue.main.async {
                    if resultBlock != nil
                    {
                        resultBlock!(true,success,error)
                    }
                }
                
                guard let error = error else
                {
                    return;
                }
                print("errorMsg:" + self.errorMessageForFails(errorCode: error._code))
            }
        }
        else
        {
            print("errorMsg:" + self.errorMessageForFails(errorCode:(useableError?.code)! ))
            
            if resultBlock != nil
            {
                resultBlock!(false,false,useableError)
            }
        }
    }

测试demo:
swift:https://github.com/zmubai/TouchIDTest-swift
object-c:https://github.com/zmubai/TouchIDTest-OC

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容