import Foundation import Security /// 用于安全存储和检索敏感信息(如令牌)的 Keychain 帮助类 public class KeychainHelper { // Keychain 键名 private enum KeychainKey: String, CaseIterable { case accessToken = "com.memorywake.accessToken" case refreshToken = "com.memorywake.refreshToken" } /// 保存访问令牌到 Keychain public static func saveAccessToken(_ token: String) -> Bool { return save(token, for: .accessToken) } /// 从 Keychain 获取访问令牌 public static func getAccessToken() -> String? { return get(for: .accessToken) } /// 保存刷新令牌到 Keychain public static func saveRefreshToken(_ token: String) -> Bool { return save(token, for: .refreshToken) } /// 从 Keychain 获取刷新令牌 public static func getRefreshToken() -> String? { return get(for: .refreshToken) } /// 删除所有存储的令牌 public static func clearTokens() { delete(for: .accessToken) delete(for: .refreshToken) } /// 清除所有存储的 Keychain 数据 public static func clearAll() { // 清除所有已知的 keychain 项 KeychainKey.allCases.forEach { key in delete(for: key) } // 额外清理:删除所有通用密码项(作为安全措施) let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecReturnAttributes as String: true, kSecMatchLimit as String: kSecMatchLimitAll ] var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) if status == errSecSuccess, let items = result as? [[String: Any]] { for item in items { if let account = item[kSecAttrAccount as String] as? String, let service = item[kSecAttrService as String] as? String { let deleteQuery: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: account, kSecAttrService as String: service ] SecItemDelete(deleteQuery as CFDictionary) } } } } // MARK: - 私有方法 private static func save(_ string: String, for key: KeychainKey) -> Bool { guard let data = string.data(using: .utf8) else { return false } let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key.rawValue, kSecValueData as String: data ] // 先删除已存在的项目 SecItemDelete(query as CFDictionary) // 添加新项目 let status = SecItemAdd(query as CFDictionary, nil) return status == errSecSuccess } private static func get(for key: KeychainKey) -> String? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key.rawValue, kSecReturnData as String: true, kSecMatchLimit as String: kSecMatchLimitOne ] var dataTypeRef: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef) guard status == errSecSuccess, let data = dataTypeRef as? Data else { return nil } return String(data: data, encoding: .utf8) } private static func delete(for key: KeychainKey) { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key.rawValue ] SecItemDelete(query as CFDictionary) } }