美文网首页
iOS KeyChain钥匙串使用

iOS KeyChain钥匙串使用

作者: Fxxxxxxx | 来源:发表于2017-12-08 11:21 被阅读65次

前言

对于iOS的数据持久化,小的数据我们使用最多的肯定是UserDefaults,但是UserDefaults的弊端也比较明显:存取都是重写整个文件,效率低下、无法存储太大的数据、安全性低,很容易被攻破;当然这些只是题外话,我们今天要讲的KeyChain钥匙串,虽然也不能存储太大的数据,但是它的安全性很高,还可以随着iCould同步,苹果官方也推荐我们使用这个 来存储账号密码等重要的信息,那我们来看看怎么使用KeyChain。

keyChain

image.png
keyChain的存储的信息都是一个个的字典,每个字典作为一个Item,item可以指定为以上类型,官方的类型解释为:

@enum Class Value Constants
@discussion Predefined item class constants used to get or set values in a dictionary. The kSecClass constant is the key and its value is one of the constants defined here. Note: on Mac OS X 10.6, only items of class kSecClassInternetPassword are supported.
@constant kSecClassInternetPassword Specifies Internet password items.
@constant kSecClassGenericPassword Specifies generic password items.
@constant kSecClassCertificate Specifies certificate items.
@constant kSecClassKey Specifies key items.
@constant kSecClassIdentity Specifies identity items.

我们普通账号密码就使用kSecClassGenericPassword(一般密码)就好,keyChain的常用方法如下

public func SecItemCopyMatching(_ query: CFDictionary, _ result: UnsafeMutablePointer<CoreFoundation.CFTypeRef?>?) -> OSStatus
public func SecItemAdd(_ attributes: CFDictionary, _ result: UnsafeMutablePointer<CoreFoundation.CFTypeRef?>?) -> OSStatus
public func SecItemUpdate(_ query: CFDictionary, _ attributesToUpdate: CFDictionary) -> OSStatus
public func SecItemDelete(_ query: CFDictionary) -> OSStatus

顾名思义,分别是查找、添加、更新和删除,直接将我们设置好的Item的字段作为参数传入就可以调用,不过我们这里适当封装一下,便于我们使用

工具封装

代码如下

//
//  KeyChainTool.swift
//
//  Created by Fxxx on 2017/12/6.
//  Copyright © 2017年 Fxxx. All rights reserved.
//

import UIKit

class KeyChainTool: NSObject {
    
    //工具单例类
    static let shared = KeyChainTool()
    
    //item字典
    private func keyChainQueryDictionaryWithKey(key: String) -> NSMutableDictionary {
        
        let dic = NSMutableDictionary()
        dic.setObject(kSecClassGenericPassword, forKey: kSecClass as! NSCopying)
        dic.setObject(kSecAttrAccessibleAfterFirstUnlock, forKey: kSecAttrAccessible as! NSCopying)
        dic.setObject(key, forKey: kSecAttrService as! NSCopying)
        dic.setObject(key, forKey: kSecAttrAccount as! NSCopying)
        return dic
        
    }
    
    //新增
    func addToKeyChain(data: Any, key: String) -> Bool {
        
        let dic = self.keyChainQueryDictionaryWithKey(key: key)
        SecItemDelete(dic)
        dic.setObject(NSKeyedArchiver.archivedData(withRootObject: data), forKey: kSecValueData as! NSCopying)
        let status = SecItemAdd(dic, nil)
        if status == noErr {
            return true
        }
        return false
        
    }
    
    //更新
    func updateData(data: Any, key: String) -> Bool {
        
        let oldDic = self.keyChainQueryDictionaryWithKey(key: key)
        guard SecItemCopyMatching(oldDic, nil) == noErr else {
            return false
        }
        let updateDic = NSMutableDictionary()
        updateDic.setObject(NSKeyedArchiver.archivedData(withRootObject: data), forKey: kSecValueData as! NSCopying)
        let status = SecItemUpdate(oldDic, updateDic)
        if status == errSecSuccess {
            return true
        }
        return false
        
    }
    
    //获取
    func getDataForKey(key: String) -> Any? {
        
        var result: Any?
        let dic = self.keyChainQueryDictionaryWithKey(key: key)
        dic.setObject(kCFBooleanTrue, forKey: kSecReturnData as! NSCopying)
        dic.setObject(kSecMatchLimitOne, forKey: kSecMatchLimit as! NSCopying)
        var queryResult: CFTypeRef?
        if SecItemCopyMatching(dic, &queryResult) == noErr {
            
            if queryResult != nil {
                result = NSKeyedUnarchiver.unarchiveObject(with: queryResult! as! Data)
            }
            
        }
        return result
        
    }
    
    //删除
    func removeDataForKey(key: String) -> Bool {
        
        let dic = self.keyChainQueryDictionaryWithKey(key: key)
        let status = SecItemDelete(dic)
        if status == noErr {
            return true
        }
        return false
        
    }
    
}

这里我使用了单例模式来优化,将主要的功能进行了封装,具体的使用操作在结尾Demo里,欢迎下载查看

Group分享

image.png
要想在不同的应用之间共享我们钥匙串中的数据,必须为我们的Keychain设置Group,在Capabilities里,打开Keychain Sharing,打开后我可以看到Group默认就是我们的Bundle ID,如果要分享给其他应用,在这里点击“+”添加那个应用的Bundle ID就好。

PS:之前看到很多教程,使用Keychain即使不分享也要打开Keychain Sharing,这里我试过了,不打开也不影响本地的存储和使用的,我使用的是Xcode9和swift4.

Demo下载

KeyChainDemo

相关文章

网友评论

      本文标题:iOS KeyChain钥匙串使用

      本文链接:https://www.haomeiwen.com/subject/twwsixtx.html