导语:使用登录屏幕保护应用程序是保护用户数据的绝佳方法,你可以使用内置于iOS的钥匙串来确保数据的安全。 另外,苹果还通过生物识别技术,比如人脸ID和触摸ID提供了另一层保护。
使用登录屏幕保护应用程序是保护用户数据的绝佳方法,你可以使用内置于iOS的钥匙串来确保数据的安全。 另外,苹果还通过生物识别技术,比如人脸ID和触摸ID提供了另一层保护。
从iPhone 5S开始,生物识别数据就被存储在采用了64位架构的苹果A7处理器和最新芯片的安全区域。这就意味着你可以轻松地将处理登录信息的任务交给钥匙串以及人脸ID或触摸ID。
在本文中,你将从静态身份验证开始。一步一步地使用钥匙串来存储和验证登录信息。最后,你将在你的应用中学会如何使用触摸ID或人脸ID。
请注意:人脸ID需要你在物理设备上进行测试。触摸ID现在可以在模拟器的Xcode 9中模拟。钥匙串也可以在模拟器中使用。在本文中,在大多数情况下,我都是针对触摸ID进行介绍的,不过这个过程也适用于人脸ID,因为其底层的 LocalAuthentication框架都是相同的。
前期准备工作
首先,请点此下载一个操作模板。
这是一个基本的笔记应用程序,使用核心数据来存储用户的操作数据,操作板上有一个登录视图,用户可以输入用户名和密码,而应用程序视图的其余部分已经相互连接并可以使用。
构建并运行以查看你的应用在当前状态下的样子:
请注意:你可以忽略关于注释类型缺失的任何编译器错误。它将由Core Data自动生成。
此时,点击“登录”按钮将简单地关闭视图并显示注释列表,你也可以从此屏幕创建新记录。点击注销就可以返回到登录视图。如果应用程序在后台使用,它会立即返回到登录视图,这可以保护数据不被查看。
在你执行其它操作之前,你应该改变包标识符(bundle identifier)并指定一个适当的“组”。
在项目导航器中选择TouchMeIn,然后选择TouchMeIn目标。在General选项卡中,将Bundle Identifier更改为使用你自己的域名,以反向域标记 (reverse-domain-notation),例如com.raywenderich.TouchMeIn。
然后,从“组”菜单中,选择与你的开发者帐户关联的组,如下所示:
完成所有的前期准备工作后,就可以编写登录代码了。
编写登录代码
此时你需要将用户提供的凭证添加进来,以对抗硬编码的值。
打开LoginViewController.swift并在managedObjectContext下方添加以下常量:
let usernameKey = "Batman" let passwordKey = "Hello Bruce!"
这些只是硬编码的用户名和密码,你将检查用户提供的凭据。
接下来,在loginAction(_:)下面添加以下方法:
func checkLogin(username: String, password: String) -> Bool { return username == usernameKey && password == passwordKey }
此时,你可以根据以前定义的常量检查用户提供的凭据。
接下来,将loginAction(_:)内容替换为以下内容:
if checkLogin(username: usernameTextField.text!, password: passwordTextField.text!) { performSegue(withIdentifier: "dismissLogin", sender: self) }
此时, 只有在凭据正确的情况下,你调用checkLogin(username:password:),才会关闭登录视图。
输入用户名Batman和密码Hello Bruce!,然后点击登录按钮,登录屏幕应该像预期的那样消失。
虽然这种简单的身份验证方法似乎可行,但它并不是非常安全,因为存储为字符串的凭据很容易被黑客利用合适的工具进行攻击。所以,密码不应直接存储在应用程序中。为此,你需要使用钥匙串来存储密码。
请注意:大多数应用程序的密码只是简单的字符串,以bullet的形式隐藏。在应用程序中处理密码的最佳方式是在捕获后立即对其进行SALT或SHA-2方式进行加密。只有用户才知道真实的字符串。
你可以查看Chris Lowe的iOS 5的基本安全性 – 第1部分来了解钥匙串如何工作。
封装过程
接下来就是在应用程序中添加一个钥匙串封装器。
在一开始下载模板的时候,你会下载了一个有用的资源文件夹。找到并打开Finder中的资源文件夹。你会看到KeychainPasswordItem.swift文件,这个类来自苹果的样本代码GenericKeychain。
将KeychainPasswordItem.swift拖进来,如下所示:
出现提示时,确保Copy items if needed和TouchMeIn选项都被选中:
构建并运行以确保没有错误,如果一切顺利,现在你就可以利用你的应用程序中的钥匙串了。
钥匙串的使用
要使用钥匙串,你首先要在其中存储用户名和密码。接下来,你将检查用户提供的凭据,以查看它们是否与钥匙串中存储的用户名和密码匹配。
你需要跟踪用户创建的凭据,以便你可以将“登录”按钮上的文本从“创建”更改为“登录”。你还会将用户名存储在用户默认值中,这样你就可以在每次执行此检查时自动执行该过程。
钥匙串需要一些配置才能正确存储你的应用程序的信息,你将以serviceName和可选的accessGroup的形式提供该配置。最终,你会使用一个结构来存储这些值。
打开LoginViewController.swift。在导入语句下方添加以下内容:
// Keychain Configuration struct KeychainConfiguration { static let serviceName = "TouchMeIn" static let accessGroup: String? = nil }
接下来,添加下面的managedObjectContext:
var passwordItems: [KeychainPasswordItem] = [] let createLoginButtonTag = 0 let loginButtonTag = 1 @IBOutlet weak var loginButton: UIButton!
passwordItems是你将传入钥匙串的KeychainPasswordItem类型的空数组,这样你将使用两个常量来确定登录按钮是否被用来创建一些凭据,以便你使用loginButton 输出口来根据其创建状态来更新登录按钮的标题。
接下来,会出现两种情况:如果点击按钮时,如果用户还没有创建凭据,按钮文本将显示“创建”;否则按钮将显示“登录”。
首先,如果登录失败,你需要选择一种方法来通知用户。什么方法呢?就是在checkLogin(username:password:)后添加以下内容:
private func showLoginFailedAlert() { let alertView = UIAlertController(title: "Login Problem", message: "Wrong username or password.", preferredStyle:. alert) let okAction = UIAlertAction(title: "Foiled Again!", style: .default) alertView.addAction(okAction) present(alertView, animated: true) }
现在,将loginAction(sender:)替换为以下内容:
@IBAction func loginAction(sender: UIButton) { // 1 // Check that text has been entered into both the username and password fields. guard let newAccountName = usernameTextField.text, let newPassword = passwordTextField.text, !newAccountName.isEmpty, !newPassword.isEmpty else { showLoginFailedAlert() return } // 2 usernameTextField.resignFirstResponder() passwordTextField.resignFirstResponder() // 3 if sender.tag == createLoginButtonTag { // 4 let hasLoginKey = UserDefaults.standard.bool(forKey: "hasLoginKey") if !hasLoginKey && usernameTextField.hasText { UserDefaults.standard.setValue(usernameTextField.text, forKey: "username") } // 5 do { // This is a new account, create a new keychain item with the account name. let passwordItem = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: newAccountName, accessGroup: KeychainConfiguration.accessGroup) // Save the password for the new item. try passwordItem.savePassword(newPassword) } catch { fatalError("Error updating keychain - (error)") } // 6 UserDefaults.standard.set(true, forKey: "hasLoginKey") loginButton.tag = loginButtonTag performSegue(withIdentifier: "dismissLogin", sender: self) } else if sender.tag == loginButtonTag { // 7 if checkLogin(username: newAccountName, password: newPassword) { performSegue(withIdentifier: "dismissLogin", sender: self) } else { // 8 showLoginFailedAlert() } } }
以下是代码中发生的进程:
1.如果用户名或密码为空,则会向用户显示一个警告并返回;
2. 如果键盘是可见的,会将它删除;
3.如果登录按钮的标签是createLoginButtonTag,则会继续创建一个新的登录;
4.接下来,你将读取来自UserDefaults的hasLoginKey,用于指示是否已将密码保存到钥匙串中。如果hasLoginKey为false并且用户名字段包含了文本,那么你就将该文本作为用户名保存到UserDefaults中。
5.使用serviceName,newAccountName(username)和accessGroup创建KeychainPasswordItem。使用Swift的错误处理,你可以尝试保存密码。如果保存密码失败,那么就是catch出现了问题。
6.然后,你将UserDefaults中的hasLoginKey设置为true,以指示密码已保存到钥匙串。你将登录按钮的标签设置为loginButtonTag以更改按钮的文本信息,以便在用户下次运行应用程序时提示用户登录,而不是提示用户创建登录。最后,关闭loginView。
7.如果用户正在登录(如loginButtonTag所示),则调用checkLogin来验证用户提供的凭证,如果他们匹配,那么你就可以关闭登录视图了。
8.如果登录认证失败,则会向用户显示一个警告消息。
func checkLogin(username: String, password: String) -> Bool { guard username == UserDefaults.standard.value(forKey: "username") as? String else { return false } do { let passwordItem = KeychainPasswordItem(service: KeychainConfiguration.serviceName, account: username, accessGroup: KeychainConfiguration.accessGroup) let keychainPassword = try passwordItem.readPassword() return password == keychainPassword } catch { fatalError("Error reading password from keychain - (error)") } }
请注意:你也许会觉得奇怪为什么不把用户名密码和UserDefaults一起存储呢?因为这是一个很糟糕的想法,因为存储在UserDefaults中的值是使用plist文件保存的。它本质上是一个驻留在应用程序库文件夹中的XML文件,因此任何人都可以读取设备的物理访问。另一方面,钥匙串使用三重数字加密标准(3DES)来加密其数据。即使有人获得这些数据,他们也将无法读取。
接下来,用以下更新的实现替换checkLogin(username:password:):
let usernameKey = "Batman" let passwordKey = "Hello Bruce!"
此时,你要检查输入的用户名是否与UserDefaults中存储的用户名相匹配,并且密码与钥匙串中存储的密码相匹配。
接下来,删除以下几行:
// 1 let hasLogin = UserDefaults.standard.bool(forKey: "hasLoginKey") // 2 if hasLogin { loginButton.setTitle("Login", for: .normal) loginButton.tag = loginButtonTag createInfoLabel.isHidden = true } else { loginButton.setTitle("Create", for: .normal) loginButton.tag = createLoginButtonTag createInfoLabel.isHidden = false } // 3 if let storedUsername = UserDefaults.standard.value(forKey: "username") as? String { usernameTextField.text = storedUsername }
现在可以根据hasLoginKey的状态,适当地设置按钮标题和标签了。
将下面的代码添加到viewDidLoad()中,然后调用super::
依次对每个编号进行注释:
1.首先你要检查hasLoginKey,看看你是否已经为该用户存储了一个登录名;
2.如果是这样,将按钮的标题更改为Login,将其标签更新为loginButtonTag,并隐藏createInfoLabel,其中包含信息文本“以创建用户名和密码开始”。如果你没有该用户的存储登录信息,则将按钮标签设置为“创建”并将createInfoLabel显示给用户。
3.最后,你需要将用户名字段设置为UserDefaults中保存的内容,以便为用户提供更方便的日志记录。
最后,需要将输出口连接到登录按钮。打开Main.storyboard并选择登录 Login View Controller Scene。按住Ctrl键,从“登录视图控制器”拖动到“登录”按钮,如下所示:
在弹出窗口中,选择loginButton:
运行时,输入你自己选择的用户名和密码,然后点击创建。
请注意,如果你忘记连接loginButton 输出口,那么你可能会看到错误的Fatal error: unexpectedly found nil while unwrapping an Optional value,如果是这样,请按照上述相关步骤连接输出口。
现在,点击注销并尝试使用相同的用户名和密码登录 ,此时你应该会看到出现的注释列表。
点击注销并尝试重新登录,不过这次使用的是不同的密码,然后点击登录。此时,你应该会看到以下警告:
现在你已经可以使用钥匙串添加身份验证了。接下来,就可以开始创建触摸ID了。
触摸ID
请注意:人脸ID要求你在物理设备(如iPhone X)上进行测试,触摸ID现在可以在模拟器中的Xcode 9中模拟。你可以在任何使用A7芯片或更新的设备和人脸ID / 触摸ID硬件的设备上测试生物识别ID。
在本节中,除了使用钥匙串之外,你还需要将生物识别ID添加到你的项目中。虽然钥匙串不需要人脸ID / 触摸ID,但对于生物识别ID失败的实例,或者对于没有Touch ID兼容设备的用户来说,实现备份身份验证就是不可能的事情了。
打开Assets.xcassets,接下来,从你在Finder中先前下载的项目中打开Resources文件夹。找到FaceIcon和Touch-icon-lg.png图像,并将它们拖到Images.xcassets中,以便Xcode知道它们是相同的图像,唯一的差别就是分辨率:
打开Main.storyboard并把对象库中的按钮拖动到堆栈视图中的“创建信息标签”下方的Login View Controller Scene中。你可以打开Document Outline,打开开合三角标识,并确保Button在堆栈视图内。如下所示:
如果你需要查看堆栈视图,请参阅UIStackView教程:介绍堆栈视图。
在“属性”检查器中,按如下所示调整按钮的属性:
1.将类型设置为自定义。
2.将标题留空。
3.将图像设置为Touch-icon-lg。
完成后,按钮的属性应如下所示:
此时,你要确保新的按钮被选中,然后单击面板布底部布局栏中的“添加新约束”按钮,并将约束条件设置如下:
宽度应该是66,身高应该是67,点击Add 2约束,操作视图现在应该如下所示。
现在,你仍然在Main.storyboard中,打开辅助编辑器并确保显示LoginViewController.swift。
接下来,就像在其它输出口一样,从你刚添加到LoginViewController.swift的按钮中选择控制标记(Control-drag)。
在弹出框中输入touchIDButton,然后单击连接:
这样,你会创建一个输出口,用于隐藏没有生物识别ID的设备上的按钮。
接下来,你需要为该按钮添加一个操作。
把来自同一个按钮的控制标记拖动到LoginViewController.swift,也就是 checkLogin(username:password:)的上面。
在弹出窗口中,将Connection更改为Action,将Name设置为touchIDLoginAction,现在将Arguments设置为none。然后点击连接。
运行以检查是否有任何错误,由于目前你尚未添加对生物识别ID的支持,因此你仍然可以创建模拟器。
添加本地认证
实现生物识别ID就像导入本地认证框架和调用一些简单而强大的方法一样简单。
以下是本地认证的解释:
“本地身份验证框架提供了用于请求用户使用指定的安全策略进行身份验证的工具”,而本文所指定的安全策略就是用户的生物识别技术 。
iOS 11的新功能支持人脸ID,LocalAuthentication添加了一些新的功能:所需的FaceIDUsageDescription和LABiometryType是用来确定设备是否支持人脸ID或触摸ID。
在Xcode的项目导航器中选择项目并单击Info选项卡,将鼠标悬停在其中一个键的右侧,然后单击+。开始输入“隐私”,然后在出现的弹出列表中选择“隐私 – 脸部ID使用说明”。
请注意:你也可以输入“NSFaceIDUsageDescription”。
这是字符串,在value字段中,你可以使用Face ID来解锁这些注释。
在项目导航器中,右键单击TouchMeIn组文件夹并选择New File,直到找到iOS Swift文件,然后点击下一步。将TouchMeIn目标文件保存为TouchIDAuthentication.swift。点击创建。
打开TouchIDAuthentication.swift并在import Foundation添加以下导入:
import LocalAuthentication
接下来,添加以下内容来创建一个新的类:
class BiometricIDAuth { }
现在你需要引用LAContext类,在这个类中,在花括号之间添加以下代码。
let context = LAContext()
用上下文引用认证上下文,这是本地认证的主要特点。你需要一个函数来查看用户设备或模拟器中是否有生物识别ID。
如果BiometricIDAuth内部支持生物特征ID,则添加以下方法以返回Bool:
func canEvaluatePolicy() -> Bool { return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) }
打开LoginViewController.swift并添加以下属性以创建对BiometricIDAuth的引用:
let touchMe = BiometricIDAuth()
在viewDidLoad()底部添加如下内容:
touchIDButton.isHidden = !touchMe.canEvaluatePolicy()
在本文,你可以使用canEvaluatePolicy(_:)来检查设备是否可以实现生物认证。如果是,则显示触摸ID按钮,如果没有,请将其隐藏。
在模拟器上构建并运行,此时你会看到触摸ID标志已隐藏。现在建立和运行你的人脸ID或触摸ID的设备,此时,你会看到触摸ID按钮被显示出来。在模拟器中,你可以从硬件菜单中依次选择触摸ID>注册并测试按钮。
面部ID或触摸ID
如果你使用的是iPhone X或更高版本的人脸ID设备,那就要注意了。因为此时,触摸ID图标 已经被处理过了,不存在了。不过,你可以使用biometryType枚举类型来解决这个问题。
打开TouchIDAuthentication.swift并在类的上方添加BiometricType枚举。
enum BiometricType { case none case touchID case faceID }
接下来,添加以下函数以使用canEvaluatePolicy返回来选择支持的生物特征类型。
func biometricType() -> BiometricType { let _ = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) switch context.biometryType { case .none: return .none case .touchID: return .touchID case .faceID: return .faceID } }
打开LoginViewController并将以下内容添加到viewDidLoad()的底部,以修复按钮的图标:
switch touchMe.biometricType() { case .faceID: touchIDButton.setImage(UIImage(named: "FaceIcon"), for: .normal) default: touchIDButton.setImage(UIImage(named: "Touch-icon-lg"), for: .normal) }
在触摸ID注册的模拟器上构建并运行,查看触摸ID图标,此时你会看到脸部ID图标显示在iPhone X上。
让触摸ID正常工作
打开TouchIDAuthentication.swift并在上下文中添加以下变量:
var loginReason = "Logging in with Touch ID"
以上解释了应用程序请求验证的原因,该原因将在显示的对话框中。
接下来,将下面的方法添加到BiometricIDAuth的底部以对用户进行身份验证。
func authenticateUser(completion: @escaping () -> Void) { // 1 // 2 guard canEvaluatePolicy() else { return } // 3 context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: loginReason) { (success, evaluateError) in // 4 if success { DispatchQueue.main.async { // User authenticated successfully, take appropriate action completion() } } else { // TODO: deal with LAError cases } } }
以下就是上述代码中发生的进程:
1.authenticateUser(completion :)以闭包的形式提供了一个完成处理程序;
2.你正在使用canEvaluatePolicy()来检查该设备是否有能力进行生物认证。
3.如果设备支持生物识别ID,则使用evaluatePolicy(_:localizedReason:reply:)开始策略评估,即提示用户进行生物识别身份验证。 评估完成后,evaluatePolicy(_:localizedReason:reply:)会进行立即回复。
4.在回复代码块中,首先处理成功案例。默认情况下,策略评估会发生在一个专用线程上,所以代码会跳转到主线程,以便它可以更新UI。如果验证成功,你将调用拒绝登录视图的segue。
打开LoginViewController.swift,找到touchIDLoginAction(_:)并将其替换为以下内容:
@IBAction func touchIDLoginAction() { touchMe.authenticateUser() { [weak self] in self?.performSegue(withIdentifier: "dismissLogin", sender: self) } }
如果用户通过身份验证,则可以关闭“登录”视图。
处理错误
如果你没有在你的设备上设置生物识别ID,那该怎么办?本地身份验证的一个重要部分是错误响应,所以框架会包含一个LAError类型,不过也有可能是从第二次使用canEvaluatePolicy获得一个错误。
此时,你会收到一个警告,告诉你出了什么问题。你需要将TouchIDAuth类的消息传递给LoginViewController。幸运的是,你可以使用完成处理程序来传递可选消息。
打开TouchIDAuthentication.swift并更新authenticateUser方法。
更改签名以包含一个可选的消息,即遇到错误时的提示消息。
func authenticateUser(completion: @escaping (String?) -> Void) {
接下来,查找// TODO:并将其替换为以下内容。
// 1 let message: String // 2 switch evaluateError { // 3 case LAError.authenticationFailed?: message = "There was a problem verifying your identity." case LAError.userCancel?: message = "You pressed cancel." case LAError.userFallback?: message = "You pressed password." case LAError.biometryNotAvailable?: message = "Face ID/Touch ID is not available." case LAError.biometryNotEnrolled?: message = "Face ID/Touch ID is not set up." case LAError.biometryLockout?: message = "Face ID/Touch ID is locked." default: message = "Face ID/Touch ID may not be configured" } // 4 completion(message)
过程如下:
1.用一个字符串来保存消息;
2.查看“失败”案例,你可以使用switch语句为每个错误情况设置相应的错误消息,然后向用户显示警告视图。
3.如果身份验证失败,则显示警告。实际上,你应该真正评估并解决返回的特定错误代码:
3.1 LAError.biometryNotAvailable:该设备不兼容人脸ID 或触摸ID;
3.2 LAError.passcodeNotSet:根据触摸ID的要求,没有启用密码;
3.3 LAError.biometryNotEnrolled:没有存储的脸部或指纹。
3.4 LAError.biometryLockout:失败的尝试太多了。
4.在completion 闭包中传递消息。
iOS会响应与LAError.passcodeNotSet和LAError.biometryNotEnrolled相关的警告。
还有一个错误需要处理,就是在guard语句的else块中添加以下内容,只需return即可。
completion("Touch ID not available")
最后要更新的是成功案例,完成后应该包含nil,这表明你没有得到任何错误,在第一个成功块中添加了nil。
completion(nil)
完成这些更改后,完成的方法应该如下所示:
func authenticateUser(completion: @escaping (String?) -> Void) { guard canEvaluatePolicy() else { completion("Touch ID not available") return } context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: loginReason) { (success, evaluateError) in if success { DispatchQueue.main.async { completion(nil) } } else { let message: String switch evaluateError { case LAError.authenticationFailed?: message = "There was a problem verifying your identity." case LAError.userCancel?: message = "You pressed cancel." case LAError.userFallback?: message = "You pressed password." case LAError.biometryNotAvailable?: message = "Face ID/Touch ID is not available." case LAError.biometryNotEnrolled?: message = "Face ID/Touch ID is not set up." case LAError.biometryLockout?: message = "Face ID/Touch ID is locked." default: message = "Face ID/Touch ID may not be configured" } completion(message) } } }
请注意:编译错误处理时,你会看到三条警告,都是关于使用的常量。这是由于苹果增加了对人脸ID的支持,以及Swift导入Objective-C头文件的方式。虽然有一些潜在的解决方法,但是它们远不如“Swift-like”。由于苹果已经意识到这个问题,并计划在未来修复这个问题,所以我想在此简单介绍一下。
打开LoginViewController.swift并更新touchIDLoginAction(_:),如下所示:
@IBAction func touchIDLoginAction() { // 1 touchMe.authenticateUser() { [weak self] message in // 2 if let message = message { // if the completion is not nil show an alert let alertView = UIAlertController(title: "Error", message: message, preferredStyle: .alert) let okAction = UIAlertAction(title: "Darn!", style: .default) alertView.addAction(okAction) self?.present(alertView, animated: true) } else { // 3 self?.performSegue(withIdentifier: "dismissLogin", sender: self) } } }
进程如下:
1.你已经更新了闭包以接受可选消息,如果生物特征ID有效,则不存在消息;
2. 如果允许打开消息并显示警告,你可以使用它;
3.如果没有消息,你可以关闭登录视图。
在物理设备上构建并运行,然后使用触摸ID测试登录。
由于LAContext处理了大部分繁重的工作,所以实现生物识别ID相对来说比较简单。作为回报,你可以在同一个应用程序中使用钥匙串和生物识别身份验证,来处理用户没有使用触摸ID的设备的事件。
请注意:如果你想要测试触摸ID中的错误,你可以尝试故意错误登录。这样做五次,你将被禁用使用触摸ID,并要求密码身份验证。这可以防止其他人控制你设备上的其他应用程序。你可以通过设置 >触摸ID和密码重新启用它。
人脸ID的使用
关于iPhone X的最酷的事情之一是使用脸部识别而不用触摸屏幕。你可以通过添加了一个可用于触发人脸ID的按钮,但也可以自动触发人脸ID。
打开LoginViewController.swift并在viewDidLoad()下面添加如下代码:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let touchBool = touchMe.canEvaluatePolicy() if touchBool { touchIDLoginAction() } }
这将验证你的设备是否支持生物识别ID,如果支持,则设备将尝试验证用户。
在装有iPhone X或人脸ID的设备上构建并运行,以测试运行是否正常。
你可以从这里下载完整的示例应用程序。
你在本文中创建的LoginViewController可以为任何需要管理用户凭证的应用程序提供参考。
你还可以添加一个新的视图控制器,或修改现有的LoginViewController,以允许用户随时更改密码。不过,这对于生物识别ID来说是不必要的,但是,你可以创建一个更新钥匙串的方法,以提示用户在修改密码时输入当前的密码。
苹果还建议在使用人脸ID时隐藏用户名、密码字段以及登录按钮。你可以在苹果的官方iOS安全指南中了解更多有关保护你的iOS应用程序的信息。