iOS开源加密相册Agony的实现(一)
简介
虽然目前市面上有一些不错的加密相册App,但不是内置广告,就是对上传的张数有所限制。本文介绍了一个加密相册的制作过程,该加密相册将包括多密码(输入不同的密码即可访问不同的空间,可掩人耳目)、WiFi传图、照片文件加密等功能。目前项目和文章会同时前进,项目的源代码可以在github上下载。
点击前往GitHub
概述
本文主要介绍加密相册的登录验证与注册模块的实现。注册时只需要密码,每个密码对应一个独立的存储空间,登录时通过Touch ID或密码验证。如果有多套密码,Touch ID会被绑定到一个主密码上(可更改)。
账户数据存储设计
账户类设计
由于加密相册只用于本地,当前设计还未考虑密码找回,因此账户只需要密码这一字段即可,为了统计当前已有账户数量,再使用一个id字段,账户类SGAccount
设计如下。
@interface SGAccount : NSObject <NSSecureCoding>@property (nonatomic, assign) NSInteger accountId;
@property (nonatomic, copy) NSString *password;@end
为了进行归档存储,需要实现NSCoding的相关方法,如下。
#import "SGAccount.h"NSString * const kSGAccountId = @"kSGAccountId";
NSString * const kSGAccountPwd = @"kSGAccountPwd";@implementation SGAccount- (void)encodeWithCoder:(NSCoder *)encoder {[encoder encodeInteger:self.accountId forKey:kSGAccountId];[encoder encodeObject:self.password forKey:kSGAccountPwd];
}- (instancetype)initWithCoder:(NSCoder *)decoder {if (self = [super init]) {self.accountId = [decoder decodeIntegerForKey:kSGAccountId];self.password = [decoder decodeObjectForKey:kSGAccountPwd];}return self;
}+ (BOOL)supportsSecureCoding {return YES;
}@end
账户集合类设计
对于多个账户,使用一个账户集合类来管理,账户集合类管理所有的账户,由于登录验证时需要查询密码对应的账户是否存在,为了高效查找,应该使用以密码为key的Map,也就是NSDictionary来存储。
除此之外,还需要记录Touch ID对应的密码,综上所述,设计如下。
@interface SGAccountSet : NSObject <NSSecureCoding>@property (nonatomic, strong) NSMutableDictionary<NSString *, SGAccount *> *accountMap;
@property (nonatomic, copy) NSString *touchIDPassword;@end
同理这些属性也需要在NSCoding的相关方法里处理,类的实现如下。
#import "SGAccountSet.h"NSString * const kSGAccountSetAccountMap = @"kSGAccountSetAccountMap";
NSString * const kSGAccountSetTouchIDPassword = @"kSGAccountSetTouchIDPassword";@implementation SGAccountSet+ (BOOL)supportsSecureCoding {return YES;
}- (instancetype)initWithCoder:(NSCoder *)decoder {if (self = [super init]) {self.accountMap = [decoder decodeObjectForKey:kSGAccountSetAccountMap];self.touchIDPassword = [decoder decodeObjectForKey:kSGAccountSetTouchIDPassword];}return self;
}- (void)encodeWithCoder:(NSCoder *)encoder {[encoder encodeObject:self.accountMap forKey:kSGAccountSetAccountMap];[encoder encodeObject:self.touchIDPassword forKey:kSGAccountSetTouchIDPassword];
}- (NSMutableDictionary<NSString *,SGAccount *> *)accountMap {if (_accountMap == nil) {_accountMap = @{}.mutableCopy;}return _accountMap;
}@end
对于accountMap的懒加载,可以保证在没有账户数据时拿到的字典不为空。
账户管理类的设计
公有接口设计
账户管理类对外提供的接口主要是注册与验证,为了方便,作为单例使用。
注册时只需提供密码即可,而验证包括两种情况,其一是通过密码验证,第二是通过Touch ID验证,当验证成功时直接返回账户类。
除此之外,账户管理类还有一个属性currentAccount记录当前验证成功的账户,以便后续使用,具体设计如下。
@interface SGAccountManager : NSObject+ (instancetype)sharedManager;
- (void)registerAccountWithPassword:(NSString *)password errorMessage:(NSString * __autoreleasing *)errorMessage;
- (SGAccount *)getAccountByPwd:(NSString *)pwd;
- (SGAccount *)getTouchIDAccount;
/** 用于AppDelegate获取窗口的根控制器* 没有注册过账户则进入注册页面* 注册过用户则进入登录验证页面*/
- (UIViewController *)getRootViewController;@property (nonatomic, strong) SGAccount *currentAccount;@end
私有接口设计
私有接口用于管理类内部的逻辑实现,其中accountSet用于存储所有用户数据,accountPath用于存储账户数据保存和加载的路径。
@interface SGAccountManager ()@property (nonatomic, strong) SGAccountSet *accountSet;
@property (nonatomic, copy) NSString *accountPath;@end
账户集合accountSet的懒加载
账户集合类的初始化包括两个步骤,首先从硬盘加载数据,如果硬盘上没有数据,则初始化一个。之所以分解为两个方法,是因为从硬盘加载数据的方法loadAccountSet会被在其他地方调用,实现如下。
- (SGAccountSet *)accountSet {if (_accountSet == nil) {[self loadAccountSet];}return _accountSet;
}
- (void)loadAccountSet {SGAccountSet *set = [NSKeyedUnarchiver unarchiveObjectWithFile:self.accountPath];if (!set) {set = [SGAccountSet new];}_accountSet = set;
}
账户存取路径accountPath的懒加载
账户数据的存储路径会在加载和写入账户集合类数据时使用,实现如下。
- (NSString *)accountPath {if (_accountPath == nil) {_accountPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"account.agony"];}return _accountPath;
}
注册的实现
注册时传入密码,密码经过加密后,先判断账户集合中是否已经存在此密码,以防止密码重复,这是因为密码与存储空间一一对应,因此密码不能重复。如果密码重复,则通过传入的字符串指针回传。
对于第一次注册的密码,将会被绑定到Touch ID上,以后使用Touch ID验证时则相当于输入此密码,注册方法的实现如下。
- (void)registerAccountWithPassword:(NSString *)password errorMessage:(NSString * __autoreleasing *)errorMessage {NSAssert(password != nil, @"password cannot be nil");// 对密码进行MD5+盐的加密处理password = [self encryptString:password];SGAccount *account = self.accountSet.accountMap[password];// 如果根据要注册的密码能取到账户,则说明密码重复,回传错误并返回if (account != nil) {*errorMessage = @"Account Already Exists";return;}account = [SGAccount new];// 生成账户idNSInteger accountid = self.accountSet.accountMap.allKeys.count + 1;account.accountId = accountid;account.password = password;// 存入到集合中self.accountSet.accountMap[password] = account;if (accountid == 1) {// 如果是第一次注册,则将其绑定到Touch ID验证对应的密码上self.accountSet.touchIDPassword = password;}// 将内存数据同步到硬盘[self saveAccountSet];
}
加密方法的实现如下。
- (NSString *)encryptString:(NSString *)string {return [[[[NSString stringWithFormat:@"allowsad12345%@62232",string] MD5] MD5] MD5];
}
MD5方法通过分类的形式添加到NSString上,实现如下。
#import "NSString+MD5.h"
#import <CommonCrypto/CommonDigest.h>@implementation NSString (MD5)- (NSString *)MD5 {const char *cStr = [self UTF8String];unsigned char digest[CC_MD5_DIGEST_LENGTH];CC_MD5(cStr, (CC_LONG)strlen(cStr), digest);NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {[result appendFormat:@"%02x",digest[i]];}return result;
}@end
将数据写入到硬盘的方法实现如下。
- (void)saveAccountSet {[NSKeyedArchiver archiveRootObject:self.accountSet toFile:self.accountPath];
}
登录验证的实现
通过密码验证的方式,先将密码加密,再与集合中的密码比对,找到匹配的则验证成功,实现如下。
- (SGAccount *)getAccountByPwd:(NSString *)pwd {pwd = [self encryptString:pwd];return self.accountSet.accountMap[pwd];
}
通过Touch ID验证的方式,需要在Touch ID验证成功后调用,使用Touch ID对应的密码进行验证,实现如下。
- (SGAccount *)getTouchIDAccount {NSString *pwd = self.accountSet.touchIDPassword;return self.accountSet.accountMap[pwd];
}
窗口根控制器选择的实现
如果已经有了账户,则返回导航控制器包裹的验证控制器SGWelcomeViewController
,如果没有注册过账户,则先初始化一个导航控制器包裹的SGWelcomeViewController
,并且向视图栈中push一个注册控制器SGRegisterViewController
,之所以这么做,是为了保证注册完成后能够返回到验证控制器,并与从验证页面进入的注册保持相同的逻辑,具体实现如下。
- (UIViewController *)getRootViewController {if ([self hasAccount]) {return [[UINavigationController alloc] initWithRootViewController:[SGWelcomeViewController new]];}SGWelcomeViewController *welcomeVc = [SGWelcomeViewController new];SGRegisterViewController *registerVc = [SGRegisterViewController new];UINavigationController *nav = [UINavigationController new];nav.viewControllers = @[welcomeVc, registerVc];return nav;
}
总结
本文主要介绍了与注册与登录验证有关的数据类和管理类的接口与实现过程,在后面的注册与登录验证视图设计中,只需要使用工具类即可。欢迎关注项目后续,项目的下载地址在本文的开头可以找到。
iOS开源加密相册Agony的实现(一)相关推荐
- iOS开源加密相册Agony的实现(二)
简介 虽然目前市面上有一些不错的加密相册App,但不是内置广告,就是对上传的张数有所限制.本文介绍了一个加密相册的制作过程,该加密相册将包括多密码(输入不同的密码即可访问不同的空间,可掩人耳目).Wi ...
- iOS开源加密相册Agony的实现(三)
简介 虽然目前市面上有一些不错的加密相册App,但不是内置广告,就是对上传的张数有所限制.本文介绍了一个加密相册的制作过程,该加密相册将包括多密码(输入不同的密码即可访问不同的空间,可掩人耳目).Wi ...
- iOS开源加密相册Agony的实现(七)
简介 虽然目前市面上有一些不错的加密相册App,但不是内置广告,就是对上传的张数有所限制.本文介绍了一个加密相册的制作过程,该加密相册将包括多密码(输入不同的密码即可访问不同的空间,可掩人耳目).Wi ...
- iOS开源加密相册Agony的实现(五)
简介 虽然目前市面上有一些不错的加密相册App,但不是内置广告,就是对上传的张数有所限制.本文介绍了一个加密相册的制作过程,该加密相册将包括多密码(输入不同的密码即可访问不同的空间,可掩人耳目).Wi ...
- iOS开源加密相册Agony的实现(四)
简介 虽然目前市面上有一些不错的加密相册App,但不是内置广告,就是对上传的张数有所限制.本文介绍了一个加密相册的制作过程,该加密相册将包括多密码(输入不同的密码即可访问不同的空间,可掩人耳目).Wi ...
- iOS开源加密相册Agony的实现(六)
简介 虽然目前市面上有一些不错的加密相册App,但不是内置广告,就是对上传的张数有所限制.本文介绍了一个加密相册的制作过程,该加密相册将包括多密码(输入不同的密码即可访问不同的空间,可掩人耳目).Wi ...
- 最好用的iOS iPhone加密相册app上线了,功能强大,完全免费
功能强大,易于使用,完全免费的iOS加密相册app上线了,AppStore下载:https://itunes.apple.com/app/id1458351572?mt=8 (有任何问题不要删除app ...
- iOS开源照片浏览器框架SGPhotoBrowser的设计与实现
简介 近日在制作一个开源加密相册时附带着设计了一个照片浏览器,在进一步优化后发布到了GitHub供大家使用,该框架虽然没有MWPhotoBrowser那么强大,但是使用起来更为方便,操作更符合常规相册 ...
- iOS开源框架和项目总结
github上关于iOS的各种开源项目集合(转) .entry-header UI 下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh ...
最新文章
- HTML中如何写for循环全选,利用html:multibox 循环遍历checkbox 并且全选全解除
- vim 中文乱码解决
- 张尧学:祝YOCSEF越办越好
- 浅谈Java网络编程之Socket (1)
- Service Worker,Web Worker,WebSocket的对比
- 为什么实际频率只有1.8G的AMD 2500+处理器运行速度比实际频率2.4G的P4-2.4B还快
- C# VS 调试 动态加载的 DLL
- Mapreduce运行过程分析(基于Hadoop2.4)——(三)
- 用flashAS3.0做一个连线题
- vs2017 C#程序打包安装部署之创建Windows安装项目
- 用python获取实时地球图像作为壁纸(windows)
- H3CTE(RS)证书挂靠,有需要的联系,扣1376443205
- 51单片机---麻雀虽小,五脏俱全
- Android端控制LED屏
- Git代码reset回滚无效解决
- csdn竟然还有这种神器!后悔没有早点知道!超好用的csdn插件,别再犹豫了,赶快入手吧!
- 默克推进下一代抗体药物偶联物疗法;美国FDA受理君实生物特瑞普利单抗治疗鼻咽癌的上市申请 | 医药健闻...
- PMP---项目经理解决冲突的8种模式,走过路过不要错过
- java智能卡开发_《Java智能卡原理与应用开发》PDF 下载
- 【Java集合】集合是什么?为什么要用集合?
热门文章
- C#开发实战1200例(第Ⅱ卷)(奋斗的小鸟)_PDF电子书
- CCF考试——201712-4行车路线
- python安装拒绝访问解决方法_使用pip安装时遇到文件拒绝访问和版本升级的问题及解决方法...
- 5.JavaScript代码优化
- 网间数据摆渡如何才能更高效?
- [python]---面向对象
- JasperReport的动态报表
- Word中添加图片时自动添加图片名
- Why Software Development Methodologies Suck?
- wps便签更改字体大小教程(2021)