如何简单地将字符串存储在钥匙串中并在需要时加载。有几种SO解决方案,主要涉及Git回购。但是我需要最新的Swift上最小,最简单的解决方案。当然,我不想添加git框架来仅在项目中存储密码。
有类似的解决方案,通过KeyChain保存和检索值对我不起作用。厌倦了编译器错误。
import Foundation import Security // Constant Identifiers let userAccount = "AuthenticatedUser" let accessGroup = "SecuritySerivice" /** * User defined keys for new entry * Note: add new keys for new secure item and use them in load and save methods */ let passwordKey = "KeyForPassword" // Arguments for the keychain queries let kSecClassValue = NSString(format: kSecClass) let kSecAttrAccountValue = NSString(format: kSecAttrAccount) let kSecValueDataValue = NSString(format: kSecValueData) let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword) let kSecAttrServiceValue = NSString(format: kSecAttrService) let kSecMatchLimitValue = NSString(format: kSecMatchLimit) let kSecReturnDataValue = NSString(format: kSecReturnData) let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne) public class KeychainService: NSObject { /** * Exposed methods to perform save and load queries. */ public class func savePassword(token: NSString) { self.save(passwordKey, data: token) } public class func loadPassword() -> NSString? { return self.load(passwordKey) } /** * Internal methods for querying the keychain. */ private class func save(service: NSString, data: NSString) { let dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! // Instantiate a new default keychain query let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue]) // Delete any existing items SecItemDelete(keychainQuery as CFDictionaryRef) // Add the new keychain item SecItemAdd(keychainQuery as CFDictionaryRef, nil) } private class func load(service: NSString) -> NSString? { // Instantiate a new default keychain query // Tell the query to return a result // Limit our results to one item let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) var dataTypeRef :AnyObject? // Search for the keychain items let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef) var contentsOfKeychain: NSString? = nil if status == errSecSuccess { if let retrievedData = dataTypeRef as? NSData { contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding) } } else { print("Nothing was retrieved from the keychain. Status code \(status)") } return contentsOfKeychain } }
KeychainService.savePassword("Pa55worD") let password = KeychainService.loadPassword() // password = "Pa55worD"
import Cocoa import Security // see https://stackoverflow.com/a/37539998/1694526 // Arguments for the keychain queries let kSecClassValue = NSString(format: kSecClass) let kSecAttrAccountValue = NSString(format: kSecAttrAccount) let kSecValueDataValue = NSString(format: kSecValueData) let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword) let kSecAttrServiceValue = NSString(format: kSecAttrService) let kSecMatchLimitValue = NSString(format: kSecMatchLimit) let kSecReturnDataValue = NSString(format: kSecReturnData) let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne) public class KeychainService: NSObject { class func updatePassword(service: String, account:String, data: String) { if let dataFromString: Data = data.data(using: String.Encoding.utf8, allowLossyConversion: false) { // Instantiate a new default keychain query let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue]) let status = SecItemUpdate(keychainQuery as CFDictionary, [kSecValueDataValue:dataFromString] as CFDictionary) if (status != errSecSuccess) { if let err = SecCopyErrorMessageString(status, nil) { print("Read failed: \(err)") } } } } class func removePassword(service: String, account:String) { // Instantiate a new default keychain query let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue]) // Delete any existing items let status = SecItemDelete(keychainQuery as CFDictionary) if (status != errSecSuccess) { if let err = SecCopyErrorMessageString(status, nil) { print("Remove failed: \(err)") } } } class func savePassword(service: String, account:String, data: String) { if let dataFromString = data.data(using: String.Encoding.utf8, allowLossyConversion: false) { // Instantiate a new default keychain query let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue]) // Add the new keychain item let status = SecItemAdd(keychainQuery as CFDictionary, nil) if (status != errSecSuccess) { // Always check the status if let err = SecCopyErrorMessageString(status, nil) { print("Write failed: \(err)") } } } } class func loadPassword(service: String, account:String) -> String? { // Instantiate a new default keychain query // Tell the query to return a result // Limit our results to one item let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) var dataTypeRef :AnyObject? // Search for the keychain items let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef) var contentsOfKeychain: String? if status == errSecSuccess { if let retrievedData = dataTypeRef as? Data { contentsOfKeychain = String(data: retrievedData, encoding: String.Encoding.utf8) } } else { print("Nothing was retrieved from the keychain. Status code \(status)") } return contentsOfKeychain } }
您需要想象以下内容连接到文本输入字段和标签,然后连接四个按钮,每种方法一个。
class ViewController: NSViewController { @IBOutlet weak var enterPassword: NSTextField! @IBOutlet weak var retrievedPassword: NSTextField! let service = "myService" let account = "myAccount" // will only work after @IBAction func updatePassword(_ sender: Any) { KeychainService.updatePassword(service: service, account: account, data: enterPassword.stringValue) } @IBAction func removePassword(_ sender: Any) { KeychainService.removePassword(service: service, account: account) } @IBAction func passwordSet(_ sender: Any) { let password = enterPassword.stringValue KeychainService.savePassword(service: service, account: account, data: password) } @IBAction func passwordGet(_ sender: Any) { if let str = KeychainService.loadPassword(service: service, account: account) { retrievedPassword.stringValue = str } else {retrievedPassword.stringValue = "Password does not exist" } } }
迅捷的Kosuke 版本 5
import Security class KeyChain { class func save(key: String, data: Data) -> OSStatus { let query = [ kSecClass as String : kSecClassGenericPassword as String, kSecAttrAccount as String : key, kSecValueData as String : data ] as [String : Any] SecItemDelete(query as CFDictionary) return SecItemAdd(query as CFDictionary, nil) } class func load(key: String) -> Data? { let query = [ kSecClass as String : kSecClassGenericPassword, kSecAttrAccount as String : key, kSecReturnData as String : kCFBooleanTrue!, kSecMatchLimit as String : kSecMatchLimitOne ] as [String : Any] var dataTypeRef: AnyObject? = nil let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef) if status == noErr { return dataTypeRef as! Data? } else { return nil } } class func createUniqueID() -> String { let uuid: CFUUID = CFUUIDCreate(nil) let cfStr: CFString = CFUUIDCreateString(nil, uuid) let swiftString: String = cfStr as String return swiftString } } extension Data { init<T>(from value: T) { var value = value self.init(buffer: UnsafeBufferPointer(start: &value, count: 1)) } func to<T>(type: T.Type) -> T { return self.withUnsafeBytes { $0.load(as: T.self) } } }
用法示例:
let int: Int = 555 let data = Data(from: int) let status = KeyChain.save(key: "MyNumber", data: data) print("status: ", status) if let receivedData = KeyChain.load(key: "MyNumber") { let result = receivedData.to(type: Int.self) print("result: ", result) }