版權(quán)聲明
本文由陳懷哲首發(fā)自簡書:http://www.reibang.com/users/9f2e536b78fd/latest_articles;
微信公眾號:陳懷哲(chenhuaizhe2016)听哭;
無需授權(quán)即可轉(zhuǎn)載却嗡,但請自覺保留以上版權(quán)聲明。
如果要實現(xiàn)自動登錄歌馍,不必每次打開應(yīng)用都去登錄,我們勢必要把密碼保存到本地湾碎。
一般我們的操作是:
每次打開應(yīng)用后尸闸,如果存在密碼,直接進(jìn)入界面兵拢,然后再進(jìn)行后臺密碼驗證翻斟。如果沒網(wǎng)絡(luò),我們可以跳過驗證说铃;如果有網(wǎng)絡(luò)访惜,我們可以后臺去驗證帳號密碼的正確性,并根據(jù)服務(wù)器的response做一些操作腻扇。
</br>
為什么直接把密碼存儲在NSUserDefaults中不安全债热?
</br>
iOS中沙盒有哪幾個文件夾,都是用來干嗎的
默認(rèn)情況下幼苛,每個沙盒含有3個文件夾:Documents, Library 和 tmp窒篱。因為應(yīng)用的沙盒機制,應(yīng)用只能在幾個目錄下讀寫文件
- Documents:蘋果建議將程序中建立的或在程序中瀏覽到的文件數(shù)據(jù)保存在該目錄下舶沿,iTunes備份和恢復(fù)的時候會包括此目錄
- Library:存儲程序的默認(rèn)設(shè)置或其它狀態(tài)信息墙杯;
- Library/Caches:存放緩存文件,iTunes不會備份此目錄括荡,此目錄下文件不會在應(yīng)用退出刪除
- tmp:提供一個即時創(chuàng)建臨時文件的地方高镐。
獲取到沙盒Library路徑
//獲取Library目錄路徑
- (void)getLibraryPath
{
NSArray * pathArray = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask, YES);
NSString * libraryStrPath = [pathArray objectAtIndex:0];
NSLog(@"LibraryPath:%@“,libraryStrPath);
}
如圖就是NSUserDefaults對應(yīng)的plist文件在sandbox中的位置
如果sandbox被破解,或者你的手機被越獄一汽,那么就能輕松拿到這個文件避消。
那么就能輕松讀到存儲的信息,密碼就會不安全:
這對于其它保存在NSUserDefaults中的信息也是一樣的召夹,所以對于存在NSUserDefaults中的東西岩喷,最好混淆加密一下再存儲。
如何刪除NSUserDefaults對應(yīng)的plist文件监憎?
其實就是刪除plist文件中所有的鍵值對纱意。
NSUserDefaults *userDefatluts = [NSUserDefaults standardUserDefaults];
NSDictionary *dictionary = [userDefaults dictionaryRepresentation];
for(NSString* key in [dictionary allKeys]){
[userDefaults removeObjectForKey:key];
[userDefaults synchronize];
}
</br>
如何解決“直接把密碼存儲在NSUserDefaults中不安全”的問題?
把密碼加密后再存儲到NSUserDefaults中
iOS中提供了很多種加密算法鲸阔,對于存儲密碼偷霉,可以使用不可逆的MD5加密迄委。
使用MD5加密需要導(dǎo)入頭文件:
''#import <CommonCrypto/CommonDigest.h>
##### 簡單的MD5加密
+ ( NSString *)md5String:( NSString *)str
{
const char *myPasswd = [str UTF8String ];
unsigned char mdc[ 16 ];
CC_MD5 (myPasswd, ( CC_LONG ) strlen (myPasswd), mdc);
NSMutableString *md5String = [ NSMutableString string ];
for ( int i = 0 ; i< 16 ; i++) {
[md5String appendFormat : @"%02x" ,mdc[i]];
}
return md5String;
}
##### 復(fù)雜一些的MD5加密
+ ( NSString *)md5String:( NSString *)str
{
const char *myPasswd = [str UTF8String ];
unsigned char mdc[ 16 ];
CC_MD5 (myPasswd, ( CC_LONG ) strlen (myPasswd), mdc);
NSMutableString *md5String = [ NSMutableString string ];
[md5String appendFormat : @"%02x" ,mdc[ 0 ]];
for ( int i = 1 ; i< 16 ; i++) {
[md5String appendFormat : @"%02x" ,mdc[i]^mdc[ 0 ]];
不使用NSUserDefaults保存密碼,使用keyChain來保存密碼
更加保險的方法是把密碼保存在iOS提供的keychina中类少,并且刪除應(yīng)用后叙身,密碼不會刪除,下載安裝還能使用硫狞。iOS系統(tǒng)提供了一些方法信轿,進(jìn)行一些簡單的封裝之后,就可以很方便的使用残吩。
Github-chenhuaizhe-iOS-keychain
你也可以在這里直接下載财忽,更多交流可以關(guān)注我的微博:@陳懷哲
下面是封裝代碼,使用時需要先導(dǎo)入Security.framework:
PassWordTool.h
#import <Foundation/Foundation.h>
@interface PassWordTool : NSObject
/**
* @brief 存儲密碼
*
* @param password 密碼內(nèi)容
*/
+(void)savePassWord:(NSString *)password;
/**
* @brief 讀取密碼
*
* @return 密碼內(nèi)容
*/
+(id)readPassWord;
/**
* @brief 刪除密碼數(shù)據(jù)
*/
+(void)deletePassWord;
@end
PassWordTool.m
import "PassWordTool.h"
import "KeychainTool.h"
@implementation PassWordTool
static NSString * const KEY_IN_KEYCHAIN = @"com.chenyuan.app.userid";
static NSString * const KEY_PASSWORD = @"com.chenyuan.app.password";
+(void)savePassWord:(NSString *)password
{
NSMutableDictionary *usernamepasswordKVPairs = [NSMutableDictionary dictionary];
[usernamepasswordKVPairs setObject:password forKey:KEY_PASSWORD];
[KeychainTool save:KEY_IN_KEYCHAIN data:usernamepasswordKVPairs];
}
+(id)readPassWord
{
NSMutableDictionary *usernamepasswordKVPair = (NSMutableDictionary *)[KeychainTool load:KEY_IN_KEYCHAIN];
return [usernamepasswordKVPair objectForKey:KEY_PASSWORD];
}
+(void)deletePassWord
{
[KeychainTool delete:KEY_IN_KEYCHAIN];
}
@end
KeychainTool.h
#import <Foundation/Foundation.h>
@interface KeychainTool : NSObject
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service ;
+ (void)save:(NSString *)service data:(id)data ;
+ (id)load:(NSString *)service ;
+ (void)delete:(NSString *)service ;
@end
KeychainTool.m
import "KeychainTool.h"
@implementation KeychainTool
(NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass,
service, (__bridge_transfer id)kSecAttrService,
service, (__bridge_transfer id)kSecAttrAccount,
(__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,(__bridge_transfer id)kSecAttrAccessible,
nil];
}(void)save:(NSString *)service data:(id)data {
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((__bridge_retained CFDictionaryRef)keychainQuery, NULL);
}(id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Configure the search setting
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData];
[keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:(__bridge_transfer id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
return ret;
}(void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
}
@end
#### 服務(wù)器密碼驗證登錄請求
驗證請求時泣侮,最好是不直接把明文密碼包含在請求里面即彪。
可以根據(jù)一系列字符串生成MD5加密后的簽名,根據(jù)user-id 和 簽名來驗證登錄活尊。
比如:
NSString *sourceStr = [NSString stringWithFormat:@"attach=iOS&chartset=utf-8&format=json&partner=google&userid=%@&password=%@”,userid,password];
NSString *signStr = [NSString md5String:sourceStr];
這樣得到的signStr和userid再作為網(wǎng)絡(luò)請求的參數(shù)傳給服務(wù)器做驗證隶校。