从KeyChain保存并加载 Swift
如何简单地在钥匙串中存储一个string,并在需要时加载。 有几个SO解决scheme主要是指Git回购。 但是我需要最新的Swift上最小和最简单的解决scheme。 当然,我不想在我的项目中添加git框架来存储密码。
也有类似的解决scheme通过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"
SWIFT 4:版本更新并删除密码
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, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) 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, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) // 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 } }
你需要想象下面连接到一个文本input字段和一个标签,然后有四个button连接起来,每个方法一个。
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" } } }