我的郵件:m4email@163.com 如果有這篇文章對您有幫助就點下推薦或者隨意評論一個唄,謝謝謝謝,隨便轉載,標明出處就好。
Keychain 使用? ---為了實用最大化我覺得我應該直接先說使用!
當然是使用第三方庫啦:sskeychain 3000+星星的庫不開玩笑。github地址:https://github.com/soffes/sskeychain
導入完之后首先,編譯一下有無錯。
如果是自己手動導入:
1.把SSKeychain.h SSKeychain.m SSKeychainQuery.h SSKeychainQuery.m 復制到工程
2.添加Security.framework 怎么添加?點一下那個+
3.SSKeychain.h有錯?把SSKeychain.h 中的#import <SSKeychain/SSKeychainQuery.h> 換成 #import <Foundation/Foundation.h> #import "SSKeychainQuery.h" 吧。
還有錯?作為小白我的也不知道了,發我郵件一起討論吧。
接下來演示4個過程
基本說明:儲存的數據有三個 1.服務名(這個方便對賬號密碼進行分類)2.賬號3.密碼 而這三個數據都是NSString (如果要存其他類型呢,請看后面吧)
所用到的API :
添加和更新都用這個: + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account ;
查詢密碼:+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
刪除:+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
1.添加一條鑰匙 (這個鑰匙的信息 由 服務名+賬號+密碼 組成)
記得添加頭文件
#import "SSKeychain.h"
#import "SSKeychainQuery.h"
//先定義一下要用的東東
NSString *serviceName= @"com.keychaintest.data";
NSString *account = @"m4abcd";
NSString *password = @"12345678";
//加入鑰匙串!
if ([SSKeychain setPassword:password forService:serviceName account:account]) {
NSLog(@"success !");
}
說明:就是這么簡單咯。
2.查詢
1.查詢某service 下 count 的密碼並且打印出來:
NSLog(@"%@",[SSKeychain passwordForService:serviceName account:account]);
2.查詢service下所有鑰匙:
NSArray *keys = [SSKeychain accountsForService:serviceName];
這是我的輸出:
2016-03-04 15:08:43.785 keychaintest[31342:4403403] (
{
acct = m4abcd;
agrp = test;
cdat = "2016-03-03 07:10:58 +0000";
mdat = "2016-03-04 07:08:43 +0000";
pdmn = ak;
svce = "com.keychaintest.data";
sync = 0;
tomb = 0;
}
)
說明:返回的結果為數組,數組成員就是我們查詢的鑰匙,這里只有一個鑰匙,而鑰匙信息以字典的形式構建的,鍵acct 就是count,鍵svce 就是serviceName。密碼在哪里?用方法1去取吧騷年!
3.查詢本appkeychain的所有鑰匙
NSArray *keys = [SSKeychain allAccounts];
3.更新
if([SSKeychain setPassword:@"321321" forService:serviceName account:account]){
NSLog(@"set success!");
}
4.刪除
if([SSKeychain deletePasswordForService:serviceName account:account]){
NSLog(@"delete success!");
}
說明:刪除就是把這一條鑰匙刪除哦,不是只刪除密碼!
另外的說明:如果你的password 是NSData
查詢: + (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;
設置or更新:+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;
下面開始淺淺的理解還有對蘋果API進行一點點說明吧
1.Keychain 是什么?
keychain 就是放鑰匙櫃子!就是蘋果提供給我們的一個保險櫃。
這篇文章僅針對iOS。
在iOS中每個APP 都有屬於自己的Keychain,最常用就是保存用戶的賬戶和密碼,就是記住密碼,放在這里很安全(蘋果負責幫我們加密再存起來,如果出了問題怪他咯!),假如用NSUserDefault 保存這些秘密數據,生成的plist文件(就放在那個Library/Preferences 下)容易被拿到,而且還要自己做加密。
特性:1.當app刪除了,又再次重新安裝,這個保險櫃里的信息還存在哦。 所以當你的某女同學登了APP並保存了密碼,你重裝了APP,如果不刪除記錄,你女票還是可以發現的。
2.安全!作為小白的我並不知道它實際上是存在哪里的。
2.Keychain 組成?
1.組成部分由 {N個標簽(屬性) + 一個重要數據} 組成!
2.結構可以看成是一個字典的形式大概是這樣的: @{@"屬性key1":@"屬性值1",@"屬性keyN":@"屬性值N",@"valueData":@數據}
3.內容說明:
一個重要數據:就是密碼password!
N個標簽:也是屬性,都是用來表明這條鑰匙的,如我們的serviceName ,account 都是屬性,他們對應的鍵為 kSecAttrAccount 和 kSecAttrAccount,還有系統給我們加的創建時間,修改時間等還有label,type,port,你自己打開文件進去看看吧,這些標簽的任務就是來表明這條鑰匙是獨一無二的。
3.原始API操作
先來看看幾個API
添加鑰匙: OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
查詢密碼與查詢標簽: OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
更新鑰匙信息: OSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
刪除鑰匙: OSStatus SecItemDelete(CFDictionaryRef query)
先說明一下 這些API的關鍵在於1.是理解和配置好這個操作字典 2.注意返回的OSStatus 狀態 3.CF對象與OC 之間的bridge
1.先來一發查找
過程:
1.(關鍵)先配置一個操作字典內容有:
kSecAttrService(屬性),kSecAttrAccount(屬性) 這些屬性or標簽是查找的依據
kSecReturnData(值為@YES 表明返回類型為data),kSecClass(值為kSecClassGenericPassword 表示重要數據為“一般密碼”類型) 這些限制條件是返回結果類型的依據
2.然后用查找的API 得到查找狀態和返回數據(密碼)
3.最后如果狀態成功那么將數據(密碼)轉換成string 返回
//用原生的API 實現查詢密碼
- (NSString *)passwordForService:(nonnull NSString *)service account:(nonnull NSString *)account{
//生成一個查詢用的 可變字典
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:4];
[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; //表明為一般密碼可能是證書或者其他東西
[dict setObject:service forKey:(__bridge id)kSecAttrService]; //輸入service
[dict setObject:account forKey:(__bridge id)kSecAttrAccount]; //輸入account
[dict setObject:@YES forKey:(__bridge id)kSecReturnData]; //返回Data
//查詢
OSStatus status = -1;
CFTypeRef result = NULL;
status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result);//核心API 查找是否匹配 和返回密碼!
if (status != errSecSuccess) { //判斷狀態
return nil;
}
//返回數據
NSString *password = [[NSString alloc] initWithData:(__bridge_transfer NSData *)result encoding:NSUTF8StringEncoding];//轉換成string
return password;
}
說明:其實關鍵就在於這個操作字典的配置上!
2.添加&更新
說明:當添加的時候我們一般需要判斷一下當前鑰匙串里面是否已經存在我們要添加的鑰匙。如果已經存在我們就更新好了,不存在再添加,所以這兩個操作一般寫成一個函數搞定吧。
過程關鍵:1.檢查是否已經存在 構建的查詢用的操作字典:kSecAttrService,kSecAttrAccount,kSecClass(標明存儲的數據是什么類型,值為kSecClassGenericPassword 就代表一般的密碼)
2.添加用的操作字典: kSecAttrService,kSecAttrAccount,kSecClass,kSecValueData
3.更新用的操作字典1(用於定位需要更改的鑰匙):kSecAttrService,kSecAttrAccount,kSecClass
操作字典2(新信息)kSecAttrService,kSecAttrAccount,kSecClass ,kSecValueData
//用原生的API 添加一條鑰匙
-(BOOL)addItemWithService:(NSString *)service account:(NSString *)account password:(NSString *)password{
//先查查是否已經存在
//構造一個操作字典用於查詢
NSMutableDictionary *searchDict = [[NSMutableDictionary alloc]initWithCapacity:4];
[searchDict setObject:service forKey:(__bridge id)kSecAttrService]; //標簽service
[searchDict setObject:account forKey:(__bridge id)kSecAttrAccount]; //標簽account
[searchDict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];//表明存儲的是一個密碼
OSStatus status = -1;
CFTypeRef result =NULL;
status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDict, &result);
if (status == errSecItemNotFound) { //沒有找到則添加
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; //把password 轉換為 NSData
[searchDict setObject:passwordData forKey:(__bridge id)kSecValueData]; //添加密碼
status = SecItemAdd((__bridge CFDictionaryRef)searchDict, NULL); //!!!!!關鍵的添加API
}else if (status == errSecSuccess){ //成功找到,說明鑰匙已經存在則進行更新
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; //把password 轉換為 NSData
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:searchDict];
[dict setObject:passwordData forKey:(__bridge id)kSecValueData]; //添加密碼
status = SecItemUpdate((__bridge CFDictionaryRef)searchDict, (__bridge CFDictionaryRef)dict);//!!!!關鍵的更新API
}
return (status == errSecSuccess);
}