当前位置: 首页 > news >正文

iOS:iOS开发中用户密码保存位置

原文来自简书:http://www.jianshu.com/p/4af3b8179136/comments/1294203

 

如果要实现自动登录,不必每次打开应用都去登录,我们势必要把密码保存到本地。
一般我们的操作是:
每次打开应用后,如果存在密码,直接进入界面,然后再进行后台密码验证。如果没网络,我们可以跳过验证;如果有网络,我们可以后台去验证帐号密码的正确性,并根据服务器的response做一些操作。

 

为什么直接把密码存储在NSUserDefaults中不安全?

 iOS中沙盒有哪几个文件夹,都是用来干吗的

默认情况下,每个沙盒含有3个文件夹:Documents, Library 和 tmp。因为应用的沙盒机制,应用只能在几个目录下读写文件

  • Documents:苹果建议将程序中建立的或在程序中浏览到的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录
  • Library:存储程序的默认设置或其它状态信息;
  • Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出删除
  • tmp:提供一个即时创建临时文件的地方。

获取到沙盒Library路径

//获取Library目录路径
-  (void)getLibraryPath
 {
    NSArray * pathArray = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask, YES);
   NSString * libraryStrPath = [pathArray objectAtIndex:0];
    NSLog(@"LibraryPath:%@“,libraryStrPath);
 }

如图就是NSUserDefaults对应的plist文件在sandbox中的位置

                蓝色部分为plist文件

如果sandbox被破解,或者你的手机被越狱,那么就能轻松拿到这个文件。
那么就能轻松读到存储的信息,密码就会不安全:

 

如何删除NSUserDefaults对应的plist文件?

其实就是删除plist文件中所有的键值对。

NSUserDefaults *userDefatluts = [NSUserDefaults standardUserDefaults];
NSDictionary *dictionary = [userDefaults dictionaryRepresentation];
for(NSString* key in [dictionary allKeys]){
     [userDefaults removeObjectForKey:key];
     [userDefaults synchronize];
}

 

 

如何解决“直接把密码存储在NSUserDefaults中不安全”的问题?

把密码加密后再存储到NSUserDefaults中

iOS中提供了很多种加密算法,对于存储密码,可以使用不可逆的MD5加密。
使用MD5加密需要导入头文件:
''#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;

}

##### 复杂一些的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中,并且删除应用后,密码不会删除,下载安装还能使用。iOS系统提供了一些方法,进行一些简单的封装之后,就可以很方便的使用。

 

Github-chenhuaizhe-iOS-keychain
你也可以在这里直接下载,更多交流可以关注我的微博:@陈怀哲

下面是封装代码,使用时需要先导入Security.framework:

 

PassWordTool.h

#import <Foundation/Foundation.h>

@interface PassWordTool : NSObject
/**
 *    @brief    存储密码
 *
 *    @param     password     密码内容
 */
+(void)savePassWord:(NSString *)password;

/**
 *    @brief    读取密码
 *
 *    @return    密码内容
 */
+(id)readPassWord;

/**
 *    @brief    删除密码数据
 */
+(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

 

服务器密码验证登录请求

验证请求时,最好是不直接把明文密码包含在请求里面。
可以根据一系列字符串生成MD5加密后的签名,根据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再作为网络请求的参数传给服务器做验证。

 

其他参考:

ios 利用钥匙串保存密码和获取密码

直接使用Security框架读写钥匙串,参考:http://useyourloaf.com/blog/2010/03/29/simple-iphone-keychain-access.html
 
我们使用第三方类SFHFKeychainUtils来操作钥匙串 ( GitHub代码下载 )

使用方法如下:

1、引入Security.framework框架。

2、引入头文件:#import"SFHFKeychainUtils.h"

3、存密码:   

   NSString *SERVICE_NAME=@"demo";
    [SFHFKeychainUtils storeUsername:@"dd"
                         andPassword:@"aa"
                      forServiceName:SERVICE_NAME
                      updateExisting:1
                               error:nil];

4、取密码:

    NSString *passWord =  [SFHFKeychainUtils getPasswordForUsername:@"dd"
                                                     andServiceName:SERVICE_NAME
                                                              error:nil];
    NSLog(@"%@",passWord);

5、删除用户:

[SFHFKeychainUtils deleteItemForUsername:@"dd" andServiceName:SERVICE_NAME error:nil];

 

 

 
    

 

相关文章:

  • 在windows下如何快速搭建web.py开发框架
  • 八个最优秀的Android Studio插件
  • 游戏保护大放送之GPK
  • 面向对象之多态
  • 老板突然走了-被诏安
  • 免费的在线Web文件管理器:Net2FTP,Pydio,eXtplorer,KodExplorer–功能强大
  • 常用笔记本规格表
  • 《构建之法》阅读梳理篇读后感
  • 如何下载一些网站本身不希望你下载的文件呢
  • 如何查看PC和Windows Mobile下蓝牙(Bluetooth)的Stack
  • 单页面应用SPA架构
  • 从Windows到Android开发
  • 使用 libssh 编写 client
  • QTP的那些事--判定页面是否存在某个文本内容
  • mac 安装maven 和改动java环境变量
  • hexo+github搭建个人博客
  • [译] 怎样写一个基础的编译器
  • 《剑指offer》分解让复杂问题更简单
  • es6
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • python_bomb----数据类型总结
  • Python十分钟制作属于你自己的个性logo
  • Unix命令
  • 聊聊sentinel的DegradeSlot
  • 聊聊springcloud的EurekaClientAutoConfiguration
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 算法-插入排序
  • 一个SAP顾问在美国的这些年
  • Spring Batch JSON 支持
  • ​MySQL主从复制一致性检测
  • ​卜东波研究员:高观点下的少儿计算思维
  • ​油烟净化器电源安全,保障健康餐饮生活
  • "无招胜有招"nbsp;史上最全的互…
  • # Panda3d 碰撞检测系统介绍
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • #微信小程序:微信小程序常见的配置传旨
  • $().each和$.each的区别
  • $forceUpdate()函数
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (C++)八皇后问题
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (办公)springboot配置aop处理请求.
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (排序详解之 堆排序)
  • (转) 深度模型优化性能 调参
  • (转)jQuery 基础
  • .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)...
  • .NET CORE 3.1 集成JWT鉴权和授权2
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)