我正在尝试在登录过程中使用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}},此代码可能意味着几件事,包括-50
,errSecNoSuchClass
或errSecUseKeychainListUnsupported
。
我正在关注Apple文档中的osstatus。我不太确定如何从这里继续。有什么想法吗?
答案 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)!)