-
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 注册通知
/*
AppDelegate 中可以不移除
*/
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(loginSuccess)
name:HQUserLoginSuccessedNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(logout)
name:HQUserLogoutNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(touchIDAuthentication)
name:HQTouchIDAuthenticationNotification
object:nil];
// 直接用户登录
// 如果成功,进入主页
[[NetworkTools sharedNetworkTools] userLoginWithTouchID:(BOOL)YES Failed:^{
// 如果失败,进入登录页面
[self logout];
}];
return YES;
}
/// 登录成功
- (void)loginSuccess {
// 显示主页面
[self switchStroyboard:@"Home"];
}
/// 用户注销
- (void)logout {
// 显示登录页面
[self switchStroyboard:@"Login"];
}
/// 指纹验证
- (void)touchIDAuthentication {
[self switchStroyboard:@"TouchID"];
}
/// 切换显示页面
- (void)switchStroyboard:(NSString *)sbName {
UIStoryboard *sb = [UIStoryboard storyboardWithName:sbName bundle:nil];
self.window.rootViewController = sb.instantiateInitialViewController;
}
-
HomeViewController.m
#import "NetworkTools.h"
/// 用户注销
- (IBAction)logout:(UIButton *)sender {
[[NetworkTools sharedNetworkTools] userLogout];
}
-
LoginViewController.m
#import "NetworkTools.h"
@property (weak, nonatomic) IBOutlet UITextField *usernameText;
@property (weak, nonatomic) IBOutlet UITextField *pwdText;
@property (weak, nonatomic) IBOutlet UISwitch *savePwdSwitch;
- (void)viewDidLoad {
[super viewDidLoad];
[self loadUserInfo];
}
/// 用户登录
- (IBAction)postLogin {
NetworkTools *tools = [NetworkTools sharedNetworkTools];
tools.username = self.usernameText.text;
tools.pwd = self.pwdText.text;
tools.isSavePwd = self.savePwdSwitch.isOn;
[tools userLoginWithTouchID:(BOOL)NO Failed:^{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示"
message:@"用户名或密码错误"
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alertController animated:YES completion:nil];
}];
}
/// 加载用户信息
- (void)loadUserInfo {
self.usernameText.text = [NetworkTools sharedNetworkTools].username;
self.pwdText.text = [NetworkTools sharedNetworkTools].pwd;
}
/// 键盘回收
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
-
TouchIDViewController.m
#import "NetworkTools.h"
- (void)viewDidLoad {
[super viewDidLoad];
[self touchIDAuthentication];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self touchIDAuthentication];
}
/// 指纹验证
- (void)touchIDAuthentication {
NetworkTools *tools = [NetworkTools sharedNetworkTools];
[tools touchIDAuthenticationFailed:^{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示"
message:@"指纹验证失败,请重试"
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alertController animated:YES completion:nil];
}];
}
-
NetworkTools.h
extern NSString * const HQUserLoginSuccessedNotification;
extern NSString * const HQUserLogoutNotification;
extern NSString * const HQTouchIDAuthenticationNotification;
@interface NetworkTools : NSObject
/// 用户名
@property (nonatomic, copy) NSString *username;
/// 用户口令
@property (nonatomic, copy) NSString *pwd;
/// 是否记住密码
@property (nonatomic, assign) BOOL isSavePwd;
/// 单例方法
+ (instancetype)sharedNetworkTools;
/// 用户登录
- (void)userLoginWithTouchID:(BOOL)touch Failed:(void (^)())failed;
/// 用户注销
- (void)userLogout;
/// 用户指纹验证
- (void)touchIDAuthenticationFailed:(void (^)())failed;
@end
-
NetworkTools.m
#import "NSString+TimePwd.h"
#import "SSKeychain.h"
#import <LocalAuthentication/LocalAuthentication.h>
// 定义通知字符串常量
NSString * const HQUserLoginSuccessedNotification = @"HMUserLoginSuccessedNotification";
NSString * const HQUserLogoutNotification = @"HMUserLogoutNotification";
NSString * const HQTouchIDAuthenticationNotification = @"HQTouchIDAuthenticationNotification";
@implementation NetworkTools
/**
单例创建中,使用 allocWithZone, copyWithZone ... 等等方法,会把所有创建第二个实例可能性全部堵死。
在真正开发中,有的时候,会需要额外创建一个副本。
*/
+ (instancetype)sharedNetworkTools {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
// 第一次被实例化的时候,加载用户信息
[self loadUserLoginInfo];
}
return self;
}
/// 用户登录
/**
程序启动直接执行,如果登录成功,进入主界面。如果登录失败,进入登录页面。
*/
- (void)userLoginWithTouchID:(BOOL)touch Failed:(void (^)())failed {
NSString *username = self.username;
// md5 + HMAC + 时间戳 加密
NSString *pwd = [self.pwd timeMD5StringWithKey:@"qianqian"];
NSURL *url = [NSURL URLWithString:@"http://192.168.88.200/login/loginhmac.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [[NSString stringWithFormat:@"username=%@&password=%@", username, pwd]
dataUsingEncoding:NSUTF8StringEncoding];
[[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
if (error == nil || data != nil) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
dispatch_async(dispatch_get_main_queue(), ^{
if ([dict[@"userId"] intValue] > 0) {
[self saveUserLoginInfo];
if (touch == NO || [UIDevice currentDevice].systemVersion.floatValue < 8.0) {
// 发送登录成功通知
[[NSNotificationCenter defaultCenter] postNotificationName:HQUserLoginSuccessedNotification object:nil];
} else {
// 发送指纹验证通知
[[NSNotificationCenter defaultCenter] postNotificationName:HQTouchIDAuthenticationNotification object:nil];
}
} else {
if (failed != nil) {
// 执行失败回调
failed();
}
}
});
}
}] resume];
}
/// 用户注销
- (void)userLogout {
[SSKeychain deletePasswordForService:[NSBundle mainBundle].bundleIdentifier account:self.username];
// 发送注销通知
[[NSNotificationCenter defaultCenter] postNotificationName:HQUserLogoutNotification object:nil];
}
/// 指纹验证
- (void)touchIDAuthenticationFailed:(void (^)())failed {
// 实例化本地身份验证上下文
LAContext *context= [[LAContext alloc] init];
// 判断是否支持指纹识别
if (![context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:NULL]) {
// 发送登录成功通知
[[NSNotificationCenter defaultCenter] postNotificationName:HQUserLoginSuccessedNotification object:nil];
return;
}
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:@"请验证已有指纹"
reply:^(BOOL success, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
// 输入指纹开始验证,异步执行
if (success) {
// 发送登录成功通知
[[NSNotificationCenter defaultCenter] postNotificationName:HQUserLoginSuccessedNotification object:nil];
} else {
switch (error.code) {
case -1:
if (failed != nil) {
// 执行失败回调
failed();
}
break;
case -2:
case -3:
case -4:
case -5:
case -6:
case -7:
// 发送注销通知
[[NSNotificationCenter defaultCenter] postNotificationName:HQUserLogoutNotification object:nil];
break;
default:
break;
}
}
});
}];
}
/// 用户登录信息存储
- (void)saveUserLoginInfo {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:self.username forKey:@"userNameKey"];
[userDefaults setBool:self.isSavePwd forKey:@"isSavePwdKey"];
[userDefaults synchronize];
if (self.isSavePwd) {
// 将密码保存到钥匙串中
[SSKeychain setPassword:self.pwd forService:[NSBundle mainBundle].bundleIdentifier account:self.username];
} else {
self.pwd = nil;
[SSKeychain deletePasswordForService:[NSBundle mainBundle].bundleIdentifier account:self.username];
}
}
- (void)loadUserLoginInfo {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
self.username = [userDefaults objectForKey:@"userNameKey"];
self.isSavePwd = [userDefaults boolForKey:@"isSavePwdKey"];
// 将密码从钥匙串中取出
self.pwd = [SSKeychain passwordForService:[NSBundle mainBundle].bundleIdentifier account:self.username];
}
@end
-
NSString+Hash.m
#import <CommonCrypto/CommonCrypto.h>
/// 时间戳散列函数
- (NSString *)timeMD5StringWithKey:(NSString *)key {
NSString *hmacKey = key.md5String;
NSString *hmacStr = [self hmacMD5StringWithKey:hmacKey];
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"yyyy-MM-ddHH:mm";
NSString *dateStr = [fmt stringFromDate:[NSDate date]];
hmacStr = [hmacStr stringByAppendingString:dateStr];
return [hmacStr hmacMD5StringWithKey:hmacKey];
}
/// HMAC 散列函数
- (NSString *)hmacMD5StringWithKey:(NSString *)key {
const char *keyData = key.UTF8String;
const char *strData = self.UTF8String;
uint8_t buffer[CC_MD5_DIGEST_LENGTH];
CCHmac(kCCHmacAlgMD5, keyData, strlen(keyData), strData, strlen(strData), buffer);
return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}
/// 散列函数
- (NSString *)md5String {
const char *str = self.UTF8String;
uint8_t buffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), buffer);
return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}
/// 助手方法
- (NSString *)stringFromBytes:(uint8_t *)bytes length:(int)length {
NSMutableString *strM = [NSMutableString string];
for (int i = 0; i < length; i++) {
[strM appendFormat:@"%02x", bytes[i]];
}
return [strM copy];
}