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

iOS Keychain存储读取accessToken、refreshToken报错如何解决

代码问题分析与修正方案

核心错误原因

  • 存储类型选择错误:你使用了kSecClassKey作为存储类型,这个类型仅适用于存储加密密钥(如RSA密钥、AES对称密钥),普通字符串token应该使用通用密码类型kSecClassGenericPassword,这是导致store返回-50(参数错误)、load返回-25300(条目不存在)的根本原因
  • 存储/查询参数不匹配:
    • 存储时错误使用kSecValueRef字段,普通字符串需要转成Data后存入kSecValueData字段
    • 查询时额外添加了kSecAttrKeyTypeRSA、kSecReturnRef等仅适用于密钥存储的参数,既不符合存储类型要求,也会导致查询条件和存储的条目不匹配
  • 缺少重复存储处理逻辑:如果同一个key已经存在于Keychain中,直接调用SecItemAdd会报错,需要先查询是否存在,存在则走更新逻辑,不存在再走新增逻辑

修正后的完整代码

import Security

enum Key: String {
    case accessToken = "some.keys.accessToken"
    case refreshToken = "some.keys.refreshToken"
}

enum KeychainError: Error {
    case storeFailed
    case loadFailed
    case conversionFailed
}

func store(key: Key, value: String) throws {
    guard let valueData = value.data(using: .utf8) else {
        throw KeychainError.conversionFailed
    }
    
    // 先查询是否已存在对应条目
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: key.rawValue
    ]
    
    let status = SecItemCopyMatching(query as CFDictionary, nil)
    if status == errSecSuccess {
        // 已存在则更新
        let updateAttributes = [kSecValueData as String: valueData]
        let updateStatus = SecItemUpdate(query as CFDictionary, updateAttributes as CFDictionary)
        guard updateStatus == errSecSuccess else {
            print("Update key: '\(key.rawValue)' in Keychain failed with status: \(updateStatus)")
            throw KeychainError.storeFailed
        }
    } else if status == errSecItemNotFound {
        // 不存在则新增
        let addQuery: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key.rawValue,
            kSecValueData as String: valueData
        ]
        let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
        guard addStatus == errSecSuccess else {
            print("Store key: '\(key.rawValue)' in Keychain failed with status: \(addStatus)")
            throw KeychainError.storeFailed
        }
    } else {
        print("Query key existence failed with status: \(status)")
        throw KeychainError.storeFailed
    }
}

func load(key: Key) throws -> String? {
    let getQuery: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: key.rawValue,
        kSecReturnData as String: true,
        kSecMatchLimit as String: kSecMatchLimitOne
    ]
    
    var item: CFTypeRef?
    let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
    guard status == errSecSuccess, let data = item as? Data, let value = String(data: data, encoding: .utf8) else {
        if status != errSecItemNotFound {
            print("Load key: '\(key.rawValue)' in Keychain failed with status: \(status)")
        }
        throw KeychainError.loadFailed
    }
    return value
}

可选优化

如果需要实现跨设备iCloud同步、访问控制限制(比如仅锁屏后可访问),可以在存储的query中额外添加对应的kSecAttrSynchronizable、kSecAttrAccessible参数即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.13 19:51:02