You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 20:01:33