快速钥匙串更新只有在第二次尝试时才起作用

问题描述 投票:0回答:1

您好,我在更新存储在钥匙串中的登录信息方面遇到了 iOS 钥匙串的一个非常奇怪的问题。因此,如果没有保存的凭据,则正确运行保存函数会保存登录信息。如果登录信息已存在并且用户更新了密码,则更新功能仅正确更新密码。但是,如果登录信息存在并且我尝试更改电子邮件(同时保留或更改密码),则第一次更新将失败。我必须手动单击更新登录两次才能更新登录信息。我尝试了下面的代码,只是强制删除和保存函数运行两次,同时在之间添加延迟,但这不起作用。唯一有效的是按两次“更新”。我很感激任何帮助。谢谢。

 delete(email: result.0)
 save(email: email, password: password)
 Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { _ in
      delete(email: result.0)
      save(email: email, password: password)
 }
    func save(email: String, password: String) {
        let passwordData = password.data(using: .utf8)!
        
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: "https://hustle.page",
            kSecAttrAccount as String: email,
            kSecValueData as String: passwordData
        ]
        let saveStatus = SecItemAdd(query as CFDictionary, nil)
        if saveStatus == errSecDuplicateItem {
            update(email: email, password: password)
        }
    }
    func update(email: String, password: String) {
        if let result = read(service: "https://hustle.page"){
            if result.0 == email {
                let query: [String: Any] = [
                    kSecClass as String: kSecClassGenericPassword,
                    kSecAttrService as String: "https://hustle.page",
                    kSecAttrAccount as String: email
                ]
                let passwordData = password.data(using: .utf8)!
                let updatedData: [String: Any] = [
                    kSecValueData as String: passwordData
                ]
                
                SecItemUpdate(query as CFDictionary, updatedData as CFDictionary)
            } else {
                delete(email: result.0)
                save(email: email, password: password)
                Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { _ in
                    delete(email: result.0)
                    save(email: email, password: password)
                }
            }
        }
    }
    func delete(email: String) {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: "https://hustle.page",
            kSecAttrAccount as String: email
        ]
        SecItemDelete(query as CFDictionary)
    }
    func read(service: String) -> (String, String)? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecReturnAttributes as String: true,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]
        
        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        
        if status == errSecSuccess, let item = result as? [String: Any] {
            if let account = item[kSecAttrAccount as String] as? String,
               let passwordData = item[kSecValueData as String] as? Data,
               let password = String(data: passwordData, encoding: .utf8) {
               return (account, password)
            }
        }
        return nil
    }  

in the view: 
     Button {
       save(email: email, password: password)
     } label: {
       Text("Update")
     }
ios swift keychain
1个回答
0
投票

iOS 钥匙串是一种安全的数据存储,其行为是同步的,因为它将在执行另一个请求之前执行并完成一个请求。因此,快速连续的请求可能并不总是按预期工作,特别是在不检查错误的情况下(我确实建议检查错误在我之前的答案中)。

如果您想更新现有的钥匙串条目,您需要删除旧条目,然后添加包含更新数据的新条目。
您还可以通过 Kamil Makowski 的“增强 iOS 上的用户数据安全:仔细查看钥匙串

”查看常见操作的说明

正如我上面提到的,始终检查钥匙串函数的返回值(

SecItemAdd
SecItemUpdate
SecItemDelete
)。他们将为您提供有关操作是否成功或可能发生什么错误的信息。

您可以重写

save
函数以检查包含所提供电子邮件的条目是否已存在。

  • 如果是,请删除旧条目并添加新条目。
  • 如果没有,只需添加新条目即可。

例如:

func save(email: String, password: String) {
    // Check if there's an existing entry with the provided email
    if let existing = read(service: "https://hustle.page"), existing.0 == email {
        // Delete the old entry
        let deleteStatus = delete(email: email)
        if deleteStatus != errSecSuccess {
            // Handle the delete error
            print("Error deleting data: \(deleteStatus)")
            return
        }
    }
    
    // Now, save the new data
    let passwordData = password.data(using: .utf8)!
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: "https://hustle.page",
        kSecAttrAccount as String: email,
        kSecValueData as String: passwordData
    ]
    
    let saveStatus = SecItemAdd(query as CFDictionary, nil)
    if saveStatus != errSecSuccess {
        // Handle the save error
        print("Error saving data: \(saveStatus)")
    }
}

func delete(email: String) -> OSStatus {
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: "https://hustle.page",
        kSecAttrAccount as String: email
    ]
    
    let status = SecItemDelete(query as CFDictionary)
    return status
}

在你看来:

Button {
   save(email: email, password: password)
} label: {
   Text("Update")
}

这样,每次调用

save
函数时,它都会更新现有条目或添加新条目,确保钥匙串始终与提供的数据同步。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.