版本记录
版本号 | 时间 |
---|---|
V1.0 | 2020.09.07 星期一 |
前言
在这个信息爆炸的年代,特别是一些敏感的行业,比如金融业和银行卡相关等等,这都对
app
的安全机制有更高的需求,很多大公司都有安全 部门,用于检测自己产品的安全性,但是及时是这样,安全问题仍然被不断曝出,接下来几篇我们主要说一下app
的安全机制。感兴趣的看我上面几篇。
1. APP安全机制(一)—— 几种和安全性有关的情况
2. APP安全机制(二)—— 使用Reveal查看任意APP的UI
3. APP安全机制(三)—— Base64加密
4. APP安全机制(四)—— MD5加密
5. APP安全机制(五)—— 对称加密
6. APP安全机制(六)—— 非对称加密
7. APP安全机制(七)—— SHA加密
8. APP安全机制(八)—— 偏好设置的加密存储
9. APP安全机制(九)—— 基本iOS安全之钥匙链和哈希(一)
10. APP安全机制(十)—— 基本iOS安全之钥匙链和哈希(二)
11. APP安全机制(十一)—— 密码工具:提高用户安全性和体验(一)
12. APP安全机制(十二)—— 密码工具:提高用户安全性和体验(二)
13. APP安全机制(十三)—— 密码工具:提高用户安全性和体验(三)
14. APP安全机制(十四) —— Keychain Services API使用简单示例(一)
15. APP安全机制(十五) —— Keychain Services API使用简单示例(二)
16. APP安全机制(十六) —— Keychain Services API使用简单示例(三)
17. APP安全机制(十七) —— 阻止使用SSL Pinning 和 Alamofire的中间人攻击(一)
18. APP安全机制(十八) —— 阻止使用SSL Pinning 和 Alamofire的中间人攻击(二)
开始
首先看下主要内容:
了解如何将钥匙串服务和生物特征认证集成到简单的受密码保护的
note-taking SwiftUI
应用中。本文内容来自翻译。
下面看下写作环境:
Swift 5, iOS 13, Xcode 11
Apple的钥匙串服务(Keychain Services )
是一种机制,用于以安全和受保护的方式存储小的敏感数据,例如密码,加密密钥或用户令牌(token)
。 使用钥匙串服务,您可以检查用户输入的密码是否与他们存储的密码相匹配,而不会使数据受到威胁。 但是,输入密码很繁琐! 为了解决此问题,Apple向许多设备添加了生物特征认证(biometric authentication)
。 生物特征认证允许用户使用指纹或面部扫描快速安全地确认其身份。
在本教程中,您将学习如何将钥匙串服务和生物特征认证(Keychain Services and biometric authentication)
集成到一个简单的受密码保护的note-taking SwiftUI
应用中。
打开启动项目。 构建并运行。 首次运行时,应用程序会提示您输入密码以保护您的笔记。 继续,在两个field
中键入密码,然后点击Set Password
。 您会看到一个简单的记笔记应用程序,该应用程序可让您输入文本并稍后返回。
注意:如果您忘记密码,请不要担心-运行调试版本时,会有一个神奇的按钮可将应用程序重置为初始状态。 您会丢失秘密笔记,但不会被卡住。
该应用程序包装了一个UITextView
来管理笔记。 在编辑器上方,您会看到三个按钮。 最左侧是垃圾桶按钮,它是上述的重置按钮。 右侧是一个用于更改密码的按钮,另一个是用于锁定和解锁笔记的按钮。
由于您刚刚输入了新密码,因此便笺已解锁并可以编辑。 在文本编辑器中键入一些文本。
现在停止并重新启动应用程序。该应用程序以锁定的笔记开始。编辑器窗口模糊以使内容模糊。点按锁定按钮,然后输入您先前设置的密码以解锁笔记。该应用使用User Defaults
存储您的笔记和密码。
这使得对设备进行物理访问的攻击者可以轻松找到密码!钥匙串服务(Keychain Services)
提供了一个更安全的地方来存储密码。在下一部分中,您将开始更新应用程序以执行此操作。
A Look at Keychain Services
钥匙串服务(Keychain Services)
提供了由操作系统管理的加密数据库。它用于存储密码,加密密钥,证书或任何短数据。
要将某些内容存储在钥匙串中,请打包有关数据的几个属性以及机密信息。您将它们全部存储到钥匙串项目(keychain item)
中。钥匙串服务为不同类型的item
提供类:
-
kSecClassInternetPassword
存储互联网站点的密码。 -
kSecClassGenericPassword
存储任何类型的密码。 -
kSecClassCertificate
存储证书。 -
kSecClassKey
存储一个加密密钥项。 -
kSecClassIdentity
存储identity
。
每个类使用不同的属性集。这些属性定义标识受保护项目所需的信息。它们还控制对机密信息的访问。您可以使用属性在以后搜索项目。如果需要,您可以在应用之间共享钥匙串。
Keychain Services API
已经存在了很长时间。 这意味着它是一个行之有效,可靠和安全的信息存储场所。 不幸的是,这也意味着您将要处理为C
编写的API
!
但是不要惊慌。 您将编写封装函数,以更现代的方式处理API。
Enabling Your Keychain
现在是时候设置您的钥匙串了。 您将增强该应用程序,以安全地添加,检索,更新和删除密码!
1. Adding a Password to the Keychain
在入门项目中,打开Models
组中的KeychainServices.swift
。 您将看到KeychainWrapperError
的定义,这是一个自定义Error
,将用于向用户提供反馈。
您要添加的第一件事是KeychainWrapper
的初始定义。 在KeychainWrapperError
之后,在文件末尾插入以下代码:
class KeychainWrapper {
func storeGenericPasswordFor(
account: String,
service: String,
password: String
) throws {
guard let passwordData = password.data(using: .utf8) else {
print("Error converting value to data.")
throw KeychainWrapperError(type: .badData)
}
}
}
您必须首先将密码从String
转换为Data
。 如果转换失败,则会抛出error
。
注意:添加代码时,您会看到有关“已定义但从未使用过”常量的警告:
不用担心-您将在本教程的后面部分解决这些警告。 您现在可以忽略它们。
通过query
可以访问钥匙串服务Keychain Service
。 访问“钥匙串服务”的第一步是创建一个add query
。 顾名思义,add query
定义了要存储在钥匙串中的数据。
将以下代码添加到storeGenericPasswordFor(account:service:password :)
的末尾:
// 1
let query: [String: Any] = [
// 2
kSecClass as String: kSecClassGenericPassword,
// 3
kSecAttrAccount as String: account,
// 4
kSecAttrService as String: service,
// 5
kSecValueData as String: passwordData
]
这是正在发生的事情:
- 1) 该查询是一个字典,根据属性将
String
映射到Any
对象。从Swift
调用基于C
的API
时,这种模式很常见。对于每个属性,您都提供以kSec
开头的已定义全局常量。在每种情况下,都将常量转换为String
(实际上是CFString
),然后在该属性的值之后跟随常量。 - 2) 第一键使用预定义的常数
kSecClassGenericPassword
将此项的类定义为通用密码。 - 3) 对于通用密码项目,您提供一个帐户,即您的用户名字段。您将此作为参数传递给了方法。
- 4) 接下来,为密码设置服务。这是一个任意字符串,应反映密码的用途,例如“用户登录”。您还将此作为参数传递给了方法。
- 5) 最后,使用从传递给方法的字符串转换的
passwordData
设置项目的数据。
建立查询query
后,就可以存储值了。在查询定义之后添加以下代码:
// 1
let status = SecItemAdd(query as CFDictionary, nil)
// 2
switch status {
// 3
case errSecSuccess:
break
// 4
default:
throw KeychainWrapperError(status: status, type: .servicesError)
}
这是此代码的作用:
- 1)
SecItemAdd(_:_ :)
要求钥匙串服务向钥匙串添加信息。您将query
转换为预期的CFDictionary
类型。C API
通常使用返回值来显示函数的结果。在此,值的类型为OSStatus
。 - 2) 您打开状态代码的各种值。在只检查一个值但谁知道将来会发生什么的情况下使用开关似乎有点奇怪。
- 3)
errSecSuccess
表示您的密码现在在钥匙串中。您的工作已经完成! - 4) 如果
status
包含另一个值,则函数失败。KeychainWrapperError
包含一个初始化程序,该初始化程序使用SecCopyErrorMessageString(_:_ :)
为该异常创建人类可读的消息。
您使用相同的模式来访问所有钥匙串功能:首先,创建一个查询来定义要执行的工作,然后使用该查询调用一个函数。
现在,您有了一种将密码存储在钥匙串中的方法。接下来,您将添加search
功能以查找和检索刚刚添加的项目。
2. Searching for Keychain Items
从钥匙串读取项目的步骤与添加项目的步骤相同。将以下新方法添加到KeychainWrapper
类的末尾:
func getGenericPasswordFor(account: String, service: String) throws -> String {
let query: [String: Any] = [
// 1
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecAttrService as String: service,
// 2
kSecMatchLimit as String: kSecMatchLimitOne,
// 3
kSecReturnAttributes as String: true,
kSecReturnData as String: true
]
}
再次,从钥匙串读取项目的第一步是创建适当的查询:
- 1) 在将密码添加到钥匙串时,您提供了
kSecClass,kSecAttrAccount
和kSecAttrService
。 现在,您可以使用这些值在钥匙串中找到项目。 - 2) 您可以使用
kSecMatchLimit
告诉钥匙串服务您希望将单个项目作为搜索结果。 - 3) 字典中的最后两个参数告诉
Keychain Services
返回找到的值的所有数据和属性。
query
之后,添加以下代码:
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status != errSecItemNotFound else {
throw KeychainWrapperError(type: .itemNotFound)
}
guard status == errSecSuccess else {
throw KeychainWrapperError(status: status, type: .servicesError)
}
首先,定义一个可选的CFTypeRef
变量,以保存Keychain Services
希望找到的值。 然后,您调用SecItemCopyMatching(_:_ :)
。 您提供查询和指向目标值的指针。 此函数在钥匙串中搜索匹配项并将匹配项复制到项目item
。
注意:这可能是您以前从未见过的模式。 参数前的与号
(&)
表示它是指向内存插槽的指针,而不是值本身。 C函数将使用新值更新该位置的内存。
同样,状态代码提供错误信息。 当钥匙串服务找不到所需的物品时,此代码包含特定的错误。
现在,您有了钥匙串项,但是作为CFTypeRef
。 在getGenericPasswordFor(account:service :)
的末尾添加以下代码:
// 1
guard
let existingItem = item as? [String: Any],
// 2
let valueData = existingItem[kSecValueData as String] as? Data,
// 3
let value = String(data: valueData, encoding: .utf8)
else {
// 4
throw KeychainWrapperError(type: .unableToConvertToString)
}
// 5
return value
这是正在发生的事情:
- 1) 将返回的
CFTypeRef
强制转换为字典。 - 2) 在字典中提取
kSecValueData
值并将其转换为Data
。 - 3) 尝试将数据转换回字符串,这与您存储密码时所做的操作相反。
- 4) 如果这些步骤中的任何一个返回
nil
,则表示无法读取数据。你抛出一个错误。 - 5) 如果强制转换成功,则返回包含已存储密码的字符串。
现在,您已经实现了存储和检索钥匙串物品所需的所有功能。接下来,您将连接您的用户界面,以便可以看到它的工作!
Using Keychain Services in SwiftUI
打开Models
组下的NoteData.swift
。从应用程序访问密码使用两种方法:getStoredPassword()
读取密码,而updateStoredPassword(_ :)
设置密码。还有一个属性isPasswordBlank
。
首先,在类的开头删除语句let passwordKey =“ Password”
。
现在,用以下代码替换现有的getStoredPassword()
方法:
func getStoredPassword() -> String {
let kcw = KeychainWrapper()
if let password = try? kcw.getGenericPasswordFor(
account: "RWQuickNote",
service: "unlockPassword") {
return password
}
return ""
}
此方法创建一个KeychainWrapper
并调用getGenericPasswordFor(account:service :)
读取并返回密码。 try?
表达式将异常转换为nil
。 如果搜索失败,这将导致该方法返回一个空字符串。
接下来,将updateStoredPassword(_ :)
替换为:
func updateStoredPassword(_ password: String) {
let kcw = KeychainWrapper()
do {
try kcw.storeGenericPasswordFor(
account: "RWQuickNote",
service: "unlockPassword",
password: password)
} catch let error as KeychainWrapperError {
print("Exception setting password: \(error.message ?? "no message")")
} catch {
print("An error occurred setting the password.")
}
}
您可以使用KeychainWrapper
使用相同的account
和service
来设置密码。 对于此应用,您将所有错误打印到控制台。
现在构建并运行。 该应用再次运行时要求您设置密码。 但是您的应用程序不再从UserDefaults
读取数据,这是不安全的。 而是使用加密的钥匙串来存储和检索密码。
输入新密码,您会看到之前的运行记录。 点按两次锁定按钮。 这将使用您刚设置的密码锁定然后解锁笔记。 您的密码有效,并且出现note
!
您现在可以将密码添加到钥匙串中,并且可以检索该密码以验证用户身份。 但是您仍然需要添加一些其他方法来完成应用程序中Keychain Services
的实现。
Updating a Password in the Keychain
在应用程序运行且note
解锁的情况下,点击双箭头按钮以更改密码。 输入新密码,然后点击Set Password
。 控制台窗口中出现错误:
如果该item
已经存在,则不能向钥匙串添加任何东西! 相反,您需要更新update
存储的项目。
打开KeychainServices.swift
。 在KeychainWrapper
类的末尾添加以下代码:
func updateGenericPasswordFor(
account: String,
service: String,
password: String
) throws {
guard let passwordData = password.data(using: .utf8) else {
print("Error converting value to data.")
return
}
// 1
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecAttrService as String: service
]
// 2
let attributes: [String: Any] = [
kSecValueData as String: passwordData
]
// 3
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
guard status != errSecItemNotFound else {
throw KeychainWrapperError(
message: "Matching Item Not Found",
type: .itemNotFound)
}
guard status == errSecSuccess else {
throw KeychainWrapperError(status: status, type: .servicesError)
}
}
此代码类似于您之前添加的storeGenericPasswordFor(account:service:password :)
。但是,更新需要两个词典,一个包含搜索查询,另一个具有所需的更改。细目如下:
- 1) 搜索
query
指定您要更新的数据。您提供的属性与之前创建的搜索查询一样,但是您没有使用匹配限制和返回属性之类的搜索参数。该功能将更新所有匹配的条目。 - 2) 第二个词典包含要更新的数据。您可以指定对该类有效的任何或所有属性,但仅包括要更改的属性。在这里,您仅指定新密码。但是,如果要存储这些属性的新值,则也可以设置
service or account
。 - 3)
SecItemUpdate(_:_ :)
使用上面两个词典的内容并执行更新。您会看到的最常见错误是The specified attribute does not exist
。此错误表明钥匙串服务未找到与搜索查询匹配的内容。
您不需要继续检查和决定是否需要编写新的钥匙串项或更新现有的钥匙串项,或者如果您调用错误的方法来处理出现的错误。您现在要解决此问题。
在storeGenericPasswordFor(account:service:password :)
中,找到switch status
语句并在默认值上方添加新的case
:
case errSecDuplicateItem:
try updateGenericPasswordFor(
account: account,
service: service,
password: password)
errSecDuplicateItem
是存储现有项目时返回的状态。 现在,如果您尝试存储现有item
,则可以选择更新它。
构建并运行。 使用您之前设置的密码解锁note
。 (请记住,上面的密码更改尚未完成。尝试添加已经存在的项目导致异常。)再次尝试更改密码-成功!
现在,您的应用程序可以添加,检索和更新密码。 但是仍然缺少一项动作。 您需要能够从钥匙串中删除一个值。
Deleting a Password From the Keychain
仍在KeychainServices.swift
中,将以下代码添加到KeychainWrapper
的末尾:
func deleteGenericPasswordFor(account: String, service: String) throws {
// 1
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecAttrService as String: service
]
// 2
let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess || status == errSecItemNotFound else {
throw KeychainWrapperError(status: status, type: .servicesError)
}
}
同样,此代码大部分与您之前添加的用于添加和更新item
的代码相似。 注意更改:
- 1) 尽管查询看起来很像添加和更新,但这里您没有提供新值。
注意:确保查询
query
仅标识要删除的一个或多个项目。 如果多个项目与查询query
匹配,则钥匙串服务会删除所有这些项目。
- 2) 您使用
query
调用SecItemDelete(_ :)
来删除项目。 没有撤消!
将以下代码添加到storeGenericPasswordFor(account:service:password :)
的顶部以调用新方法:
if password.isEmpty {
try deleteGenericPasswordFor(
account: account,
service: service)
return
}
现在,您可以添加,检索,更新和删除钥匙串中的项目。 这四种方法为您提供了处理钥匙串中项目所需的核心功能。
构建并运行该应用程序,然后点击“重置”按钮。 该应用程序将密码和note
设置为空字符串。 由于您刚进行了更改,因此重置按钮现在可以删除密码和note
!
重新运行该应用并设置初始密码。 点按两次锁定按钮,以确认您的新密码已将note
解锁。 现在点击箭头按钮以更改密码。 再次点击锁定按钮两次,以验证该应用是否存储了您的新密码。
恭喜你!现在,您已将钥匙串服务(Keychain Services)
添加到您的应用中以保护密码。
每次键入密码来查看note
对于用户而言都是乏味的。接下来,您将添加生物特征认证(biometric authentication)
,以实现更简单但仍安全的解锁。
Biometric Authentication in SwiftUI
生物特征认证(Biometric authentication)
使用您身体的独特特征来验证您的身份。苹果提供了两种生物特征认证方法:
- Touch ID使用您的指纹。
- Face ID使用您脸部的独特形状。
两种方法都比密码更快,更容易。而且它们更加安全。有人可能会猜出您的密码,但是复制您的指纹或面部属于间谍电影的世界,而不是现实世界!
在下一节中,您将在应用程序中内置生物特征认证。
1. Enabling Biometric Authentication in Your App
打开ToolbarView.swift
。该视图定义了编辑器上方显示的工具栏。它包括锁定按钮,用于锁定和解锁笔记。这是向您的应用程序添加生物特征认证的理想场所。您将使用Local Authentication
框架来执行此操作。
在文件的顶部,在现有import
语句之后添加以下代码:
import LocalAuthentication
LocalAuthentication
框架允许您的应用访问用户解锁设备所用的相同系统,即密码,Touch ID
或Face ID
。 当前,当note
被锁定并且用户点击锁定按钮时,该应用会将showUnlockModal
状态属性设置为true
。 将showUnlockModal
设置为true
会显示请求和验证密码的视图。
您将其更改为执行生物特征认证。 如果失败或不可用,您将退回到密码视图。
在ToolbarView.swift
中,在body
上方添加以下方法:
func tryBiometricAuthentication() {
// 1
let context = LAContext()
var error: NSError?
// 2
if context.canEvaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
error: &error) {
// 3
let reason = "Authenticate to unlock your note."
context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: reason) { authenticated, error in
// 4
DispatchQueue.main.async {
if authenticated {
// 5
self.noteLocked = false
} else {
// 6
if let errorString = error?.localizedDescription {
print("Error in biometric policy evaluation: \(errorString)")
}
self.showUnlockModal = true
}
}
}
} else {
// 7
if let errorString = error?.localizedDescription {
print("Error in biometric policy evaluation: \(errorString)")
}
showUnlockModal = true
}
}
每个步骤的运作方式如下:
- 1) 您可以使用
LAContext
对象访问生物特征认证。这将从用户交互中收集生物特征,并与设备上的Secure Enclave
通信。LocalAuthentication
可以早于Swift
并使用诸如NSError
之类的Objective-C
模式。 - 2) 您首先检查验证是否可用。第一个参数
.deviceOwnerAuthenticationWithBiometrics
,请求生物特征认证。 - 3) 您描述了为什么要在
reason
中合理使用身份验证的原因,iOS会向用户显示该身份验证。调用evalidatePolicy(_:localizedReason:reply :)
要求身份验证。设备将执行Face ID
或Touch ID
身份验证,以当前设备上可用的方式为准。调用在返回时执行该块。 - 4) 由于您是从块执行此代码并更改UI,因此必须确保更改在UI线程上运行。
- 5) 如果身份验证成功,则将
note
设置为未锁定。请注意,您没有获得有关身份验证的更多信息!您仅发现它成功或失败。 - 6) 如果身份验证失败,则打印出现在块中的任何错误。您还可以将
showUnlockModal
状态设置为true
。此操作将迫使您的应用退回到手动密码行为。 - 7) 如果初始检查失败,则表示生物特征认证不可用。您打印收到的错误,然后显示解锁视图。同样,您提供一个后备身份验证路径。
某些设备没有身份验证,或者用户可能未设置身份验证。认证有时会失败。无论失败原因是什么,您都必须始终为用户提供适当的方式来进行生物识别认证!
您需要进行另一项更改才能启用此功能。按Command-F
来找到// Biometric Authentication Point
。将下一行self.showUnlockModal = true
替换为对新方法的调用:
self.tryBiometricAuthentication()
现在您可以测试生物特征认证功能了!
2. Simulating Biometric Authentication in Xcode
幸运的是,该模拟器能够在测试过程中为您模拟生物特征认证。 您选择的模拟设备决定了生物特征认证的类型。
3. Simulating Touch ID Authentication
选择iPhone SE (2nd generation)
以测试Touch ID
。 构建并运行。 点击解锁按钮,将出现普通的解锁窗口。 如果您在控制台中查看,则会看到错误消息:No identities are enrolled
。
模拟器不会自动将设备注册为生物特征认证。 您必须进行设置。 在模拟器中,选择Features ▸ Touch ID ▸ Enrolled
。 输入密码以解锁记事。 然后点击锁定按钮两次。 这将锁定它并尝试再次将其解锁。 这次,您会看到Touch ID
提示。
要模拟生物特征认证,请使用Features ▸ Touch ID
菜单下的其他选项。 要执行成功的身份验证,请选择Features ▸ Touch ID ▸ Matching Touch
。 片刻之后,身份验证请求将消失,并且笔记将解锁。
您还可以验证身份验证失败的行为。 点击锁定按钮以锁定笔记。 再次点击以尝试身份验证。 要模拟失败的身份验证,请选择Features ▸ Touch ID ▸ Non-matching Touch
。 您会看到出现失败的提示:
在提示上选择任一选项将使身份验证失败,并显示上一个解锁页面。 输入密码以确认您仍然可以解锁该笔记。
您还可以让用户输入设备PIN
作为后备。 为此,请再次打开ToolbarView.swift
。 使用Command-F
查找context.evaluatePolicy
。 将.deviceOwnerAuthenticationWithBiometrics
更改为.deviceOwnerAuthentication
。
现在构建并运行。 身份验证失败,请注意,Enter Password
选项已更改为Enter Passcode
。 点按它,您会看到模拟器passcode entry
。 对于模拟器,任何passcode
值都可以使用。 输入密码,note
将解锁。
4. Simulating Face ID Authentication
Face ID
和Touch ID
一样。 但是Face ID
有一个新要求。 您必须将NSFaceIDUsageDescription
密钥添加到应用程序的Info.plist
中,否则授权请求将失败。
打开Info.plist
。 右键单击页面的空白部分,然后选择Add Row
。 滚动浏览键列表,然后选择Privacy – Face ID Usage Description
。 双击值列下的,然后输入To allow you to unlock your note without entering your password
。
现在,将设备更改为支持Face ID
的iPhone 11 Pro
。 构建并运行。 在模拟器中,选择Features ▸ Face ID ▸ Enrolled
。 菜单名称已更改,以反映新的生物特征认证方法。
在模拟器上的应用中,选择一个新密码。 进入便笺视图后,点击两次锁定按钮。 iOS提示您允许生物识别身份验证:
点击OK
。 现在,您会看到Face ID
提示。 选择Features ▸ Face ID ▸ Matching Face
,您会看到note
解锁。
5. Making the Authentication Method Visible to the User
现在,您将再添加一次触摸touch
,以使用户知道该应用程序支持生物识别身份验证。
在ToolbarView.swift
中,在import
语句之后直接添加以下代码:
func getBiometricType() -> String {
let context = LAContext()
_ = context.canEvaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
error: nil)
switch context.biometryType {
case .faceID:
return "faceid"
case .touchID:
// In iOS 14 and later, you can use "touchid" here
return "lock"
case .none:
return "lock"
@unknown default:
return "lock"
}
}
此代码在context
上使用biometryType
属性来确定可用的生物特征认证的类型。 它返回要匹配的SF
符号的名称,或者返回未知的当前锁定符号。 在iOS 14
之前,没有Touch ID
符号。
找到// Lock Icon
注释,然后将Image
代码更改为:
Image(systemName: noteLocked ? getBiometricType() : "lock.open")
构建并运行。 现在,锁定按钮指示它使用Face ID
进行身份验证!
您可能已经保护了密码,但是note
本身仍以纯文本形式存储在User Default
中。 查看我们的CryptoKit tutorial教程,以获取有关保护大量数据的信息。
对于其他类别的钥匙串items
,Apple的Keychain Services文档应该是您的第一站。 其他类的工作原理几乎相同,并且文档中包含其中大多数的Swift示例。
对于从UIKit
角度来看的类似材料,请阅读 How To Secure iOS User Data: The Keychain and Biometrics — Face ID or Touch ID。
以及关于钥匙串的另一个角度,请参阅 Keychain Services API Tutorial for Passwords in Swift。
后记
本篇主要讲述了
SwiftUI
的钥匙串服务和生物识别,感兴趣的给个赞或者关注~~~