1 Article|Searching for Keychain Items 文章|搜索钥匙链项目
Find keychain items based on search criteria that you specify.
根据指定的查询条件搜索钥匙链项目
2 Overview 概述
You find keychain items using a query dictionary that tells the keychain services API what item attributes to look for and what to return when a match is found. The query dictionary also lets you specify additional parameters to refine the search. For example, you can control case sensitivity when matching string attributes or limit the number of matches.
我们查询钥匙链项目时,需要使用一个查询字典来告诉钥匙链服务API,查找什么属性的项目,有项目匹配时返回什么数据.查询字典也可以让你使用特定的附加参数来精炼查询.比如,我们在匹配字符串属性时可以控制大小写敏感性或者限制匹配的数量.
As an example of conducting a search, consider the password item stored in Adding a Password to the Keychain. After providing credentials (that your app stores) for a network service, the user works with your app for a while and then eventually moves on to something else. When the user returns, your app may need to reauthenticate with the server in order to continue working. This time the app loads the password from the keychain instead of bothering the user with a login view.
实施查询的例子,考虑在 添加密码到钥匙链 一节中存储的密码.在为一个网络服务提供了用户名密码后(应用已经存储),用户使用当前应用了一段时间,然后突然去做其他事情了.当用户回来时,应用或许需要再次和服务器进行验证来继续工作.这一次,应用应该从钥匙链中获取密码,而不是用一个登录视图再次麻烦用户输入.
2.1 Create a Search Query 创建一个搜索查询
Begin the search by constructing a query dictionary:
在开始搜索之前,需要构建一个查询字典:
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
kSecAttrServer as String: server,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnAttributes as String: true,
kSecReturnData as String: true]
This query searches for Internet password items whose server attribute matches the server attribute you previously used when adding the password item. The query also limits the results to a single value (which is actually the default behavior) using the kSecMatchLimit search parameter. You receive only the first match found in the keychain, if any.
这个查询搜索类型为网络密码的项目,并且项目的服务器属性和之前添加密码项目时的相匹配.该查询还用kSecMatchLimit搜索属性限制了结果是一个值(默认也是返回一个值).如果有匹配的项目,返回第一个匹配的项目.
Finally, the query requests from the password item both its attributes and its data. You need both, because the kSecAttrAccount attribute contains the user name and the item’s data holds the password itself.
最后,查询从密码项目中同时请求了它的属性和它的数据.我们需要两者,因为kSecAttrAccount属性包含了用户名,而项目数据保存了密码.
Note注意:
By default, your app can freely retrieve its own keychain items but not those of other apps. However, keychain services does provide mechanisms for broadening or narrowing that accessibility, for example, using the kSecAttrAccessGroup attribute.
默认情况下,你的应用可以自由获取它自己保存的钥匙链项目,但是不能获取其他应用保存的钥匙链项目.然而,钥匙链服务提供了机制来扩大或者缩小访问范围,例如,使用kSecAttrAccessGroup属性.
2.2 Initiate the Search 初始化搜索
After you’ve created the query dictionary, you initiate the search with a call to the SecItemCopyMatching function:
在创建查询字典后,需要用SecItemCopyMatching函数初始化搜索.
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status != errSecItemNotFound else { throw KeychainError.noPassword }
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }
As you do with many Security framework functions, you first test the returned status value. Among other things, an error might occur if no matching item is found—for example, because you didn’t previously store a password for the given server. If an error occurs, you receive the errSecItemNotFound result, which you may want to treat differently from a general error. In fact, detecting this error is one way to recognize that you’re making your first pass through your app’s login flow, before you’ve stored anything.
处理大多数安全框架函数时,你需要首先检查返回状态的值.除此之外,如果没有匹配的项目搜索到,会触发一个错误,例如,你事先并没有为某个服务器存储密码.如果错误发生时,你会得到一个errSecItemNotFound结果,这个结果你应该和其他错误区别对待.事实上,检测此错误是在存储任何内容之前识别你正在首次通过应用程序的登录的一种方法。
When the search succeeds, the SecItemCopyMatching function provides a result through its item parameter. The type of the returned item depends on the nature of the query, as described in Item Return Result Keys.
如果搜索成功,SecItemCopyMatching函数通过item参数返回一个结果.返回的数据类型取决于查询的性质,如 项目返回结果键 中所述.
2.3 Extract the Result 提取结果
Because in your search you requested multiple return types and allowed only a single result, you should expect the result to be a dictionary. You recover the user name from the attribute value associated with the kSecAttrAccount key. Meanwhile, you use the kSecValueData key to extract the password data, which you then convert to a string:
因为在搜索中,你请求了多个返回类型,并且只允许一个结果,你应该期望结果是字典。可以从kSecAttrAccount键关联的值中恢复用户名。同时,使用kSecValueData键提取密码数据,然后将其转换为字符串:
guard let existingItem = item as? [String : Any],
let passwordData = existingItem[kSecValueData as String] as? Data,
let password = String(data: passwordData, encoding: String.Encoding.utf8),
let account = existingItem[kSecAttrAccount as String] as? String
else {
throw KeychainError.unexpectedPasswordData
}
let credentials = Credentials(username: account, password: password)
If when conducting the search you instead set the value associated with the kSecMatchLimit key to an integer number greater than 1, the returned item is an array whose entry count is limited to the value you set. Each entry in this array is formatted like the bare dictionary you obtain when limiting the matches to one. If you specify kSecMatchLimitAll for the kSecMatchLimit key, the array size is limited only by the number of matches found in the keychain.
如果在执行搜索时将kSecMatchLimit键相关联的值设置为大于1的整数,则返回的项是一个数组,其元素上限为你设置的值。此数组中的每个条目的格式与将匹配限制为一个时获得的字典类似。如果为kSecMatchLimit键指定kSecMatchLimitAll,则数组大小仅受钥匙链中的匹配数限制。
Note注意
You can’t combine the kSecReturnData and kSecMatchLimitAll options when copying password items, because copying each password item could require additional authentication. Instead, request a reference or persistent reference to the items, then request the data for only the specific passwords that you actually require.
当在复制类型为密码的项目时,不能将kSecReturnData和kSecMatchLimitAll选项组合在一起,因为复制每个密码项可能需要额外的身份验证。取而代之,请求项目的引用或持久引用,然后仅请求实际需要的特定密码的数据。
When you do get an array of results, you may need to iterate through the array for a single item of interest. You might do this by examining some other item attribute, such as the account, the creation date, or a label. On the other hand, if you know about a distinguishing characteristic ahead of time, it’s typically more efficient to narrow your initial keychain search query with the corresponding attributes.
得到一个结果数组时,你可能需要为一个感兴趣的项目遍历该数组。你可以通过检查某些其他项目属性(如帐户、创建日期或标签)来进行筛选。另一方面,如果你提前知道一个用于区别的特征,使用该属性来缩小初始钥匙链搜索查询的范围通常会更有效。
Whether or not you need to handle multiple matches at all depends on how your app uses the keychain. If you allow the user to select among different identities at runtime, you might want to store multiple passwords for a single server and then let the user select among the search results using the stored account as a selection key. Other times, you might decide to filter your search down to a single result without involving the user. If you take this approach, make sure that you don’t create similar items in the keychain—test for and then delete or modify existing items instead. See Updating and Deleting Keychain Items for more details.
是否需要处理多个匹配项目取决于应用程序如何使用钥匙链。如果允许用户在运行时在不同身份之间进行选择,则可能需要为单个服务器存储多个密码,然后让用户使用存储的帐户作为选择键来在搜索结果中进行选择。其他时候,可能会决定将搜索筛选为单个结果,而不涉及用户。如果采用此方法,请确保没有在的钥匙链中创建类似的项目,测试并删除或修改现有项。更多信息,请参考[更新和删除钥匙链项目]。
3 See Also
3.1 Keychain Item Search
SecItemCopyMatching函数
Returns one or more keychain items that match a search query, or copies attributes of specific keychain items.
返回一个或者多个和搜索查询匹配的钥匙链项目,或者特定钥匙链项目的属性拷贝.Search Attribute Keys and Values 搜索属性的键与值
Filter a keychain item search.
限定钥匙链项目的搜索.Item Return Result Keys 项目返回结果键
Specify how you want returned keychain item data formatted.
指定返回的钥匙链项目数据格式.