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

Secure Enclave Keychain存储凭证成功但读取返回空字符串问题求解

排查修复步骤

1. 修复存储逻辑缺失的更新处理

当前storeCredentials方法仅支持新增凭证,若对应server下已存在旧凭证,调用SecItemAdd会直接返回errSecDuplicateItem错误,新的凭证不会被写入,你读到的就是旧的空值数据。
修改存储逻辑,先检查是否存在旧记录,存在则走更新逻辑,不存在再走新增:

static func storeCredentials(credentials: Credentials) {
    print("Storing user credentials...")
    
    let account = credentials.email
    let password = credentials.password.data(using: .utf8)!
    let baseQuery: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                                    kSecAttrServer as String: server]
    
    // 先查询是否已存在记录
    let checkStatus = SecItemCopyMatching(baseQuery as CFDictionary, nil)
    if checkStatus == errSecSuccess {
        // 存在旧记录,执行更新
        let updateAttr: [String: Any] = [kSecAttrAccount as String: account,
                                         kSecValueData as String: password]
        let updateStatus = SecItemUpdate(baseQuery as CFDictionary, updateAttr as CFDictionary)
        if updateStatus == errSecSuccess {
            print("Successfully updated credentials")
        } else {
            let errorMsg = SecCopyErrorMessageString(updateStatus, nil) as? String ?? "未知错误"
            print("更新凭证失败,错误码:\(updateStatus),错误信息:\(errorMsg)")
        }
    } else if checkStatus == errSecItemNotFound {
        // 不存在旧记录,执行新增
        var addQuery = baseQuery
        addQuery[kSecAttrAccount as String] = account
        addQuery[kSecValueData as String] = password
        // 建议加上访问权限控制,避免后续读写异常
        addQuery[kSecAttrAccessible as String] = kSecAttrAccessibleWhenUnlocked
        let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
        if addStatus == errSecSuccess {
            print("Successfully added credentials")
        } else {
            let errorMsg = SecCopyErrorMessageString(addStatus, nil) as? String ?? "未知错误"
            print("新增凭证失败,错误码:\(addStatus),错误信息:\(errorMsg)")
        }
    } else {
        let errorMsg = SecCopyErrorMessageString(checkStatus, nil) as? String ?? "未知错误"
        print("查询旧凭证失败,错误码:\(checkStatus),错误信息:\(errorMsg)")
    }
}

2. 修复读取逻辑的错误判断

当前读取代码的状态判断写反了,会干扰排查:

// 原错误逻辑:只要不是找不到凭证,就打印“无密码”
if status != errSecItemNotFound { print("No Password") }

修改为正确的判断逻辑:

if status == errSecItemNotFound { 
    print("No Password") 
} else if status != errSecSuccess {
    let errorMsg = SecCopyErrorMessageString(status, nil) as? String ?? "未知错误"
    print("读取凭证失败,错误码:\(status),错误信息:\(errorMsg)")
}

3. 确认Keychain权限已开启

在Xcode项目设置的Signing & Capabilities标签页,添加Keychain Sharing能力,无需额外配置Keychain Group即可正常使用基础Keychain功能。如果未开启该能力,Keychain操作会返回errSecMissingEntitlement(错误码-25243)权限错误。

4. 清空历史脏数据测试

如果之前已经存入过空值凭证,可以新增清空方法先清除旧数据再测试:

static func clearOldCredentials() {
    let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                                kSecAttrServer as String: server]
    SecItemDelete(query as CFDictionary)
}

内容的提问来源于stack exchange,提问作者Saamer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 08:06:03