使用Swift将身份验证凭据添加到iOS上的钥匙串

时间:2018-09-27 23:13:55

标签: ios swift keychain

我正在尝试在登录过程中使用iOS的Keychain库(以iphone为目标)存储身份验证凭据。另外,我尝试将session_id存储在call中,将用户ID存储在kSecClassKey中。在解决了这个问题(如何在iOS中存储会话数据)之后,该解决方案似乎有点难以理解,但它似乎也是最好的解决方案。

编辑:我想我只是在使用Keychain错误。这感觉不对。如果有人在这里有任何建议,请您谅解,但我可能会尽快删除。

代码如下:

kSecAttrAccount

运行该代码时,状态显示loginUser() { ... let account = user_id let server = "www.foo.com" let token = auth_token let query: [String: Any] = [ kSecClass as String: kSecClassInternetPassword, kSecAttrAccount as String: account, kSecAttrServer as String: server, kSecClassKey as String: token ] let status = SecItemAdd(query as CFDictionary, nil) print(status) } 。根据{{​​3}},此代码可能意味着几件事,包括-50errSecNoSuchClasserrSecUseKeychainListUnsupported

我正在关注Apple文档中的osstatus。我不太确定如何从这里继续。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

仅出于学习/教学目的,我将编写如何手动使用钥匙串的方法。但是,我会考虑使用库,因为API是一个相当低级的C-API。


首先,您需要创建一个函数来检查该项目是否已经存在。 如果存在,则需要“更新”该项目。如果不存在,则需要添加它。.

下面的代码(Swift 4.2)应该可以做到。.也不需要将数据存储为InternetPassword ..您可以将其存储为通用密码..但是那部分完全取决于您。

我在下面使用了通用密码,我只存储了一个以“用户”为键的字典。

代码如下:

static let keychainIdentifier = "com.myproject.keychain.identifier"

// Checks if an item exists in the keychain already..
func exists(key: String, completion: @escaping (_ error: OSStatus, _ query: [CFString: Any], _ result: [CFString: Any]?) -> Void) {
    var query: [CFString: Any] = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrGeneric: keychainIdentifier.data(using: .utf8)!
        kSecMatchLimit: kSecMatchLimitOne,
        kSecReturnAttributes: kCFBooleanTrue,
        kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
        kSecAttrAccount: key
    ]

    var result: CFTypeRef?
    let error = SecItemCopyMatching(accountQuery as CFDictionary, &result)
    completion(error, accountQuery, result as? [CFString: Any])
}

// Adds or Updates an item in the keychain..
func setItem(key: String, data: Data) throws {
    exists(key: key) { error, query, _ in
        var query = query
        query[kSecMatchLimit] = nil
        query[kSecReturnAttributes] = nil

        if error == noErr || error == errSecDuplicateItem || error == errSecInteractionNotAllowed {
            let updateQuery = [kSecValueData: data]
            let err = SecItemUpdate(query as CFDictionary, updateQuery as CFDictionary)
            if err != noErr {
                throw KeychainError((code: err, message: "Cannot Update Item")
            }
        } 
        else if error == errSecItemNotFound {
            query[kSecValueData] = data
            let err = SecItemAdd(query as CFDictionary, nil)
            if err != noErr {
                throw KeychainError((code: err, message: "Cannot Set Item")
            }
        } else {
            if err != noErr {
                throw KeychainError(code: err, message: "Error Occurred")
            }
        }
    }
}

// Convenience function to save JSON to the keychain
func setItem(key: String, data: Codable) throws {
    let encodedData = try JSONEncoder().encode(data)
    setItem(key: key, data: encodedData)
}

及其用法:

struct User: Codable {
    let username: String
    let token: String?
    let otherInfo: String
}

let user = User(username: "Brandon", token: "...", otherInfo: "...")
setItem(key: user.username, data: user)

IMO,这使它使用起来更加容易。.但是请注意,您并不是真正应该在钥匙串中存储“大量”数据,例如整个模型或某种东西(如通用密码)。 / p>

或者,如果只是令牌,则:

setItem(key: user.username, data: "...".data(using: .utf8)!)