iOS应用中如何检测Face ID变更并触发密码验证?
检测Face ID变更的实现方案
要实现你说的「Face ID变更后强制密码验证,重新激活Face ID」的功能,核心思路是用kSecAccessControlBiometryCurrentSet标记结合Keychain存储一个验证令牌——当用户修改Face ID集合(添加/删除人脸)时,这个标记关联的Keychain条目会无法通过Face ID读取,以此间接判断Face ID是否变更。
具体步骤
1. 首次验证通过后存储令牌
当用户第一次完成Face ID验证(或密码验证)后,生成一个随机令牌(比如UUID),用带kSecAccessControlBiometryCurrentSet的访问控制项将其存入Keychain:
import Security func saveFaceIDAuthToken() -> Bool { guard let tokenData = UUID().uuidString.data(using: .utf8) else { return false } // 创建带biometryCurrentSet标记的访问控制 let accessControl = SecAccessControlCreateWithFlags( kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .biometryCurrentSet, nil ) let keychainQuery: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: "FaceID_Auth_Token", kSecAttrService: "com.your.app.bundle.id", // 替换成你的App Bundle ID kSecValueData: tokenData, kSecAttrAccessControl: accessControl! ] let status = SecItemAdd(keychainQuery as CFDictionary, nil) return status == errSecSuccess }
2. 检测Face ID是否变更
每次启动App或尝试使用Face ID前,先尝试读取Keychain里的令牌:
- 读取成功:说明Face ID集合没变化,正常用Face ID
- 读取失败(返回
errSecAuthFailed或其他无法读取的错误):说明Face ID已变更,需要强制密码验证
func hasFaceIDSetChanged() -> Bool { let keychainQuery: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: "FaceID_Auth_Token", kSecAttrService: "com.your.app.bundle.id", kSecReturnData: kCFBooleanTrue!, kSecUseOperationPrompt: "验证Face ID" ] var resultData: AnyObject? let status = SecItemCopyMatching(keychainQuery as CFDictionary, &resultData) switch status { case errSecSuccess: // 令牌读取成功,Face ID未变更 return false case errSecAuthFailed, errSecBiometryNotAvailable, errSecBiometryLockout: // 验证失败/生物特征不可用,说明Face ID已变更或被锁定 return true case errSecItemNotFound: // 令牌不存在,首次使用或已被删除,无需判定变更 return false default: // 其他未知错误,按变更处理 return true } }
3. 变更后的处理流程
当检测到Face ID变更时,先弹出密码验证界面,验证通过后删除旧令牌并重新存储新令牌,恢复Face ID使用权限:
func handleFaceIDChange() { guard hasFaceIDSetChanged() else { return } // 调用你的密码验证逻辑 guard validateUserPassword() else { // 密码验证失败,继续锁定或提示 return } // 删除旧令牌 let deleteQuery: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: "FaceID_Auth_Token", kSecAttrService: "com.your.app.bundle.id" ] SecItemDelete(deleteQuery as CFDictionary) // 存储新令牌,恢复Face ID功能 saveFaceIDAuthToken() }
关键说明
- 无法直接查询
kSecAccessControlBiometryCurrentSet状态:苹果没有提供直接获取该标记有效性的API,只能通过Keychain的读取结果间接判断。 - 为什么不用
LAContext单独判断?:LAContext只能检测Face ID是否可用、是否被锁定,但无法感知人脸集合的变更(比如添加新脸后,canEvaluatePolicy仍然返回true),必须结合Keychain的这个标记才能实现需求。
内容的提问来源于stack exchange,提问作者Amy
相关产品推荐
相关产品推荐

