美文网首页
iOS指纹使用

iOS指纹使用

作者: 山有木枝壮 | 来源:发表于2017-08-09 13:45 被阅读259次

现在大多数需要用户校验的APP都会加上指纹校验,方便简洁。

应用场景

该指纹应用到交易场景中,类似于支付宝的支付密码以及指纹密码。因为目前苹果指纹验证成功之后并不会返回任何信息,因此验证需要服务端的配置,一下是应用中指纹校验流程

指纹校验流程

关键代码

1、自己创建vc,然后调用getFingerCode即可,参数needFinger代表是否立即进行指纹校验,可以在viewDidLoad中调用

//
//  USTouchIDManager.swift
//  NewUstock
//
//  Created by amus on 2017/4/11.
//  Copyright © 2017年. All rights reserved.
//

import Foundation
import LocalAuthentication
import Toast_Swift
import RxSwift
import KeychainAccess

open class USTouchIDManager: NSObject {
//    public static let instance = USTouchIDManager()           // 做成单例block会有问题
    
    // 解绑类型: 绑定,解锁, 解绑
    enum TouchIDInputType: Int{
        case bind
        case unlock
        case unbind
    }
    
    fileprivate static let kKeyChainService = "com.amus.touchid"     // keychain service
    fileprivate static let kAPPTouchIDCode = "kAPPTouchIDCode"         // 保存在keychain中的
    fileprivate static var isShowing = false                           // 指纹识别是否已经显示出来
    
    public static var isSetCode: Variable<Bool> = Variable(false)      // 是否设置
    fileprivate var cryptFingerCode: String? = nil
    
    fileprivate var unbindBlock: (() -> Void)?
    
    public var validatedHandler: ((Bool) -> (Void))?             // 指纹验证成功之后回调
    
    public var pretendServerFingerCode = "bfd434_22ufddnf"      // 假装成后台返回的,其实是应该后台返回的,与用户相关
    
    /// 单例、外部不允许创建
//    override init() {
//        super.init()
//    }
    
    /// 指纹是否可用
    ///
    /// - Returns: 是否可用
    public static func isTouchIDAvaraible() -> Bool {
        let context = LAContext()
        var authError: NSError? = nil
        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
            return true
        } else {
            guard let error = authError as? LAError else {
                return true
            }
            return self.dealTouchErrorInfo(error: error)
        }
    }
    
    /// 有可能被锁定导致指纹不可用
    ///
    /// - Parameter error: 类型
    /// - Returns: 指纹是否可用
    fileprivate static func dealTouchErrorInfo(error: LAError) -> Bool {
        switch error.code {
        case .passcodeNotSet,
             .touchIDNotAvailable,
             .touchIDNotEnrolled:
            return false
        default:
            return true
        }
    }
    
    /// 获取指纹code,获取之后根据参数进行一次指纹绑定
    /// 该方法的功能主要是返回后台的指纹code,然后保存在keychain中
    ///
    /// - Parameter needFinger: 获取之后是否进行指纹识别,默认不进行
    public func getFingerCode(_ needFinger: Bool = false) {
        // 网络处理,这里做相关的描述
        /*
            1、获取后台的指纹code,一段与用户相关的字符串编码信息
            2、将获取的code进行md5加密
         */
        let fingerCode = self.pretendServerFingerCode          // 假装后台返回了
        self.cryptFingerCode = USTouchIDManager.getCryptFingerCode(fingerCode)
        if needFinger {
            self.bindTouckID()
        }
        
    }
    
    /// MARK: - 绑定指纹
    func bindTouckID() {
        guard let code = self.cryptFingerCode else {
            return
        }
        if USTouchIDManager.isShowing {
            return
        }
        let context = LAContext()
        context.localizedFallbackTitle = ""
        var authError: NSError? = nil
        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
            USTouchIDManager.isShowing = true
            UI.topViewController()?.view.makeToastActivity(.center)
            context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "验证指纹", reply: { (success, error) in
                USTouchIDManager.isShowing = false
                DispatchQueue.main.async {
                    UI.topViewController()?.view.hideToastActivity()
                    if success {
                        self.bindFingerCode(pwd: "", fingerCode: code)
                    }
                    else {
                        UI.topViewController()?.view.hideToastActivity()
                        guard let error = error as? LAError else {
                            return
                        }
                        
                        self.dealErrorInfo(error: error, type: .bind)
                        
                    }
                }
            })
        }
        else {
            guard let error = authError as? LAError else {
                return
            }
            DispatchQueue.main.async {
                self.dealErrorInfo(error: error, type: .bind)
            }
        }
    }
    
    /// 绑定指纹信息,这里需要跟后台一起操作
    ///
    /// - Parameters:
    ///   - pwd: 之前输入的密码
    ///   - fingerCode: 加密后的指纹code
    public func bindFingerCode(pwd: String, fingerCode: String){
        /*
            1、调用后台接口,传递指纹验证之前的密码验证,这一步也可以不要,还需要服务端返回的指纹code
            2、后台接口调用成功之后,将传递给后台的fingerCode保存在keychain中
                USTouchIDManager.saveFingerCode(fingerCode)
        */
        UI.topViewController()?.view.makeToast("绑定成功")
    }
    
    /// MARK: - 验证指纹
    
    /// 校验指纹解锁
    ///
    /// - Parameter code: 指纹code
    public func validateTouchID(_ code: String) {
        if USTouchIDManager.isShowing {
            return
        }
        let context = LAContext()
        context.localizedFallbackTitle = "交易密码"
        if #available(iOS 10.0, *) {
            context.localizedCancelTitle = "取消"
        }
        var authError: NSError? = nil
        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
            USTouchIDManager.isShowing = true
            UI.topViewController()?.view.makeToastActivity(.center)
            context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "验证指纹", reply: { (success, error) in
                USTouchIDManager.isShowing = false
                DispatchQueue.main.async {
                    UI.topViewController()?.view.hideToastActivity()
                    if success {
                        self.loginWithFingerCode(code)
                    }
                    else {
                        guard let error = error as? LAError else {
                            return
                        }
                        
                        self.dealErrorInfo(error: error, type: .unlock)
                    }
                }
            })
        }
        else {
            guard let error = authError as? LAError else {
                return
            }
            DispatchQueue.main.async {
                self.dealErrorInfo(error: error, type: .unlock)
            }
        }
    }
    
    /// 校验指纹,更新tradeToken
    ///
    /// - Parameter code: 指纹code
    fileprivate func loginWithFingerCode(_ code: String) {
        /*
            1、调用后台接口,校验指纹
            2、校验完成之后可以调用block,这样在调用改类的地方可以用block做一些事情
         */
        UI.topViewController()?.view.makeToast("校验成功")
    }
    
    
    /// 指纹解绑成功回掉
    ///
    /// - Parameter unbindBlock: block
    public func unbindSuccess(_ unbindBlock: @escaping () -> Void) {
        self.unbindBlock = unbindBlock
    }
    
    /// 解绑指纹, 可以使用交易密码解绑 -- 该方法不再使用,使用密码解绑
    /// 这个方法可以不需要了,解绑指纹,直接使用其他方式,可以是交易密码,也可以是直接点击,也可以是手机号解绑
    public func unbindTouchID() {
        let context = LAContext();
        context.localizedFallbackTitle = "交易密码"
        if #available(iOS 10.0, *) {
            context.localizedCancelTitle = "取消"
        }
        var authError: NSError? = nil;
        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
            USTouchIDManager.isShowing = true
            context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "验证指纹", reply: { (success, error) in
                USTouchIDManager.isShowing = false
                DispatchQueue.main.async {
                    if success {
                        USTouchIDManager.clearFingerCode()
                    }
                    else {
                        guard let error = error as? LAError else {
                            return
                        }
                        
                        self.dealErrorInfo(error: error, type: .unbind)
                    }
                }
            })
        }
        else {
            guard let error = authError as? LAError else {
                return
            }
            DispatchQueue.main.async {
                self.dealErrorInfo(error: error, type: .unbind)
            }
        }
    }
    
    /// 处理指纹识别,或者另一个按钮的点击
    ///
    /// - Parameters:
    ///   - error: 错误信息
    ///   - type: 指纹处理类型
    @discardableResult
    fileprivate func dealErrorInfo(error: LAError, type: TouchIDInputType = TouchIDInputType.unlock) -> Bool {
        var flag = false
        switch error.code {
        case .authenticationFailed:
            //SSLog("身份验证多次失败:  因为用户未能提供有效身份证件.")
            self.dealAuthenticationFailed(type: type)
            flag = true
        case .userCancel:
            //SSLog("身份验证被用户取消:  (例如: 点击 [取消] 按钮).")
            flag = true
        case .userFallback:
            // 输入密码后续操作
            // .deviceOwnerAuthenticationWithBiometrics 模式下点击输入密码才会触发此错误
            //SSLog("身份验证被取消:  因为用户在 \"首次验证失败后\" 的 \"第二次验证中\" 点击了 [输入密码] 按钮.")
            //USTradePasswordAlertView().show()
            // 这里会弹出指纹的输入密码(这个按钮自定义的文字,操作也在这里自定义)
            flag = true
        case .systemCancel:
//            SSLog("身份验证被系统取消:  (例如: 另一个应用程序准备切换到前台).")
            flag = true
        case .passcodeNotSet:
//            SSLog("身份验证无法启动:  因为没有在设备上设置密码 (只有设置设备的锁屏密码, 才能开启 Touch ID).")
            break
        case .touchIDNotAvailable:
//            SSLog("身份验证无法启动:  因为 Touch ID 不可用 (例如: Touch ID 损坏、设备没有指纹识别硬件模块...).")
            break
        case .touchIDNotEnrolled:
//            SSLog("身份验证无法启动:  因为没有设置指纹.")
            break
        case .touchIDLockout:
//            SSLog("身份验证失败:  因为多次尝试失败, Touch ID 被锁定, 需要通过验证锁屏密码来重新启用 Touch ID.")
            self.dealTouchIDLockout(type: type)
            flag = true
        case .appCancel:
//            SSLog("身份验证被 App 取消")
            flag = true
        default:
            flag = true
        }
        return flag
    }
    
    
    /// 处理指纹失败三次或者多次的情况
    ///
    /// - Parameter type: 进行指纹操作的类型
    fileprivate func dealAuthenticationFailed(type: TouchIDInputType = .unlock) {
        switch type {
        case .bind, .unbind:
            break
        case .unlock:
            // 指纹验证多次失败处理
//            USTradePasswordAlertView().show()
            break
        }
    }
    
    /// 处理指纹锁定需要手机密码的情况,密码输入成功,也算指纹成功(这里可以自己处理)
    fileprivate func dealTouchIDLockout(type: TouchIDInputType = .unlock) {
        switch type {
        case .bind, .unbind:
            let action1 = UIAlertAction(title: "取消", style: .cancel, handler: nil)
            let alert: UIAlertController = UIAlertController(title: "Touch ID 被锁定", message: "Touch ID 已经被锁定,请去设置中打开Touch ID", preferredStyle: .alert)
            alert.addAction(action1)
            UI.topViewController()?.us.present(alert)
        case .unlock:
            let action1 = UIAlertAction(title: "取消", style: .cancel, handler: nil)
            let action2 = UIAlertAction(title: "交易密码", style: .default, handler: { (action) in
                // 交易密码的处理,touchid锁定之后,需要其他的方式解锁
            })
            
            let alert = UIAlertController(title: "Touch ID 被锁定", message: "Touch ID 已经被锁定,请去设置中打开Touch ID或者输入密码", preferredStyle: .alert)
            alert.addAction(action1)
            alert.addAction(action2)
            UI.topViewController()?.us.present(alert)
            break
        }
    }
    
}

// MARK: - 指纹相关操作
extension USTouchIDManager {
    
    /// 根据给定的code获取md5加密后的code
    ///
    /// - Parameter originCode: 原始code
    /// - Returns: 加密后的code
    fileprivate static func getCryptFingerCode(_ originCode: String) -> String? {
        guard let cryptoCode = Utils.password(originCode) else {
            UI.topViewController()?.view.makeToast("请勿在密码中添加特殊字符")
            return nil
        }
        return cryptoCode
    }
    
    /// 获取保存在本地的指纹code,已经md5加密
    ///
    /// - Returns: 指纹code
    public static func getKeychainFingerCode() -> String? {
        let keychain = Keychain(service: kKeyChainService)
        return keychain[kAPPTouchIDCode]
    }
    
    /// 使用md5加密后保存在keychain之中的fingercode
    ///
    /// - Parameter code: 加密后的fingerCode
    public static func saveFingerCode(_ code: String) {
        let keychain = Keychain(service: kKeyChainService)
        keychain[kAPPTouchIDCode] = code
        self.isSetCode.value = true
    }
    
    /// 清除用户指纹信息
    public static func clearFingerCode() {
        let keychain = Keychain(service: kKeyChainService)
        keychain[kAPPTouchIDCode] = nil
        self.isSetCode.value = false
    }
}

注意事项

1、iOS指纹识别采用的是3+2验证,第一次验证未通过,允许用户设置一个确定按钮,确定按钮可以用来输入自己的交易密码,也可以调用系统的解锁密码。如果5次都输入失败,iOS10不会弹出系统解锁密码输入界面,需要手动再调用一次指纹。我这边的处理是直接弹出自己的交易密码。

参考文章
1、http://www.jianshu.com/p/aef5a506311b

相关文章

  • iOS 指纹识别

    iOS指纹识别只有在iPhone系统iOS8.0以上的系统才能使用。 使用指纹识别需要引用#import

  • iOS指纹使用

    现在大多数需要用户校验的APP都会加上指纹校验,方便简洁。 应用场景 该指纹应用到交易场景中,类似于支付宝的支付密...

  • ios 指纹登录 指纹支付

    ios8以后,苹果开放指纹,指纹可以用在支付,或者登录是使用 引入头文件 #import

  • iOS指纹解锁和手势解锁

    iOS指纹解锁和手势解锁 iOS指纹解锁和手势解锁

  • iOS 指纹解锁

    前言:随着指纹解锁的普及,越来越多的应用开始使用指纹解锁、指纹支付.不过作为iOS开发,这倒不难,因为苹果为我们封...

  • iOS 指纹识别

    指纹识别: iPhone5S开始,推出指纹识别 iOS8.0之后苹果允许第三方 App 使用 Touch ID进行...

  • Touch ID

    使用iOS 8 SDK添加Touch ID指纹识别功能 - Puzhi的专栏 - 博客频道 - CS...

  • iOS13.0 13.1 LAContext 无法弹出指纹UI

    iOS13.0 13.1 LAContext 无法弹出指纹UI 神坑 在升级iOS13.0 13.1时,指纹设备L...

  • iOS-金融/理财APP中常用的安全措施

    1.指纹解锁 1.1使用 TouchID的使用很简单,适用于iOS8以上,iPhone5S以上,引入框架#impo...

  • iOS 指纹验证(Touch ID)

    iOS 指纹验证(Touch ID) iOS的指纹识别是一个非常简单的api系统已经封好了,直接调起就行,非常简单...

网友评论

      本文标题:iOS指纹使用

      本文链接:https://www.haomeiwen.com/subject/ecvfrxtx.html