当前位置: 首页 > 面试题库 >

从KeyChain保存和加载| 斯威夫特

江天宇
2023-03-14
问题内容

如何简单地将字符串存储在钥匙串中并在需要时加载。有几种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"

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], 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" }
    }
}

斯威夫特5

迅捷的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)
}


 类似资料:
  • 问题内容: 我正在学习快速,并试图理解字典。我已经习惯了PHP,您可能会在其中编写以下内容… 在此示例中,将按以下顺序输出值:1、2、3 我的快速代码看起来像这样… 但是,值以意外的顺序出现。我可以做些干净的事情来保持值的创建顺序,还是不能对字典进行快速排序? 问题答案: 正如已经回答的那样,词典的要点是未排序。Swift(和Objective-C)中有三种类型的集合 数组是项目的有序列表。当项目

  • 问题内容: 什么之间的区别,以及何时该使用的呢? 例如,在Python中,如果我想打印字典,我只是,但是现在有2个其他选项。我应该如何以及何时使用它们? 问题答案: 一些区别: vs : 调试应用程序时,该功能在Xcode控制台中打印消息。 这是Swift 2中已删除且不再使用的变体。如果看到正在使用的旧代码,则可以安全地将其替换为。 回到Swift 1.x,没有在打印字符串的末尾添加换行符,而是

  • 问题内容: 为了在使用Swift时与C API集成,我需要使用sizeof函数。在C语言中,这很容易。在Swift中,我处于类型错误的迷宫中。 我有以下代码: 第二行显示错误“’NSNumber’不是’T.Type’的子类型”。为什么会这样,我该如何解决? 问题答案: 使用sizeof如下: 使用类型作为参数。 如果您想要变量的大小,可以将该字段传递给。 像这样: 或更简单地说(由user1020

  • 问题内容: NS和非NS类之间有什么区别?特别是vs ?是否代表围绕核心非功能性的某种包装器? 问题答案: Swift 3 为现有Foundation类类型引入了一些新的 覆盖值类型 ,例如for ,for 等。完整列表和详细信息可以在下面找到 SE-0069变异性和基础价值类型 一些原因是 提供适当的值语义, 而不是可变且不可变的变体, 更多的“ Swifty” API。 新的叠加层类型应提供相

  • 问题内容: 我一直在尝试实现单例,以用作我从网络上传到我的iOS应用的照片的缓存。我在下面的代码中附加了三个变体。我试图使版本2正常工作,但是它导致了我不理解的编译器错误,并希望就我做错的事情寻求帮助。变体1进行缓存,但我不喜欢使用全局变量。变体3并没有进行实际的缓存,我相信这是因为我在赋给var ic = ....的赋值中获得了副本,对吗? 任何反馈和见解将不胜感激。 谢谢Zvi 问题答案: 标

  • 问题内容: 要找到第一个,请使用: 我如何找到第二次出现? 问题答案: 项目清单 您可以按照以下步骤在剩余的数组切片中再次搜索元素的索引: 编辑/更新: Xcode 11•Swift 5.1或更高版本 测试: