iOS 实现自动登陆(从低级作法到高级作法)

建议先大体看一遍再斟酌用哪一种方法


前言

最近在重构App。旧的版本登陆模块没有自动登陆功能,体验极其很差。网上搜索了不少教程都没找到完整的,故写篇文章梳理一下。ios

  • 难点一 审核问题 若是App首次启动后,加载的是登陆界面,而且必需要登陆才能进行其余操做,那么审核将会是个问题。本文不涉及这方面的知识(反正这App打包工程师是FAFA,留给他头疼)。网上涉及这方面的文章挺多的,以前粗略了解过,最好提供测试帐号,而且提供测试视频到美国能直接看的视频网站。
  • 难点二 业务逻辑问题。 有两种实现方法。 第一种比较低级的。每次App启动都进入登陆界面,若是以前设置了自动登陆,就调用登陆按钮的方法。实现起来比较简单。 第二种是仿WX的,也是如今的主流App采用的。若是以前登陆成功过而且没有退出帐号,那么下次打开App就进入功能主界面,不进入登陆界面。 还有用来记录登陆状态的flag状态的改变(最后总结说)。
  • 难点三 👆第二种方法中的登陆令牌 token token是用来判断当前用户的登陆状态!
  • 难点四 本地密码加密(最后发现自动登陆能够不须要保存密码)

先不考虑加密 用UserDefault保存

Demo连接 https://github.com/Hsusue/iOS-AutoLogin 该Demo基于第二种方法,包含了界面逻辑、加密演示、token工具包。 第一种差很少且比较简单,如下会贴出代码。 git

演示自动登陆

第一种low的方法

  • HSUUserDefault.m封装了UserDefault的方法
+(void)saveUserDefaultObject:(id)object key:(NSString *)key
{
    NSUserDefaults *defaults =  [NSUserDefaults standardUserDefaults];
    [defaults setObject:object forKey:key];
    [defaults synchronize];
}


+(id)getUserDefaultObject:(NSString *)key
{
    NSUserDefaults *defaults =  [NSUserDefaults standardUserDefaults];
    id tempObject = [defaults objectForKey:key];
    return tempObject;
}


+(void)removeObjectWithKey:(NSString *)key
{
    NSUserDefaults *defaults =  [NSUserDefaults standardUserDefaults];
    [defaults removeObjectForKey:key];
    [defaults synchronize];
}
复制代码
  • AppDelage.m中 ,设置根控制器为登陆控制器
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    LoginVC *vc = [[LoginVC alloc] init];
    self.window.rootViewController = vc;
    [self.window makeKeyAndVisible];

    return YES;
}
复制代码
  • 登陆控制器中 根据以前的设置 布置UI 若自动登陆YES , 触发登陆按钮方法
- (void)viewDidLoad {
    self.userNameTextField.text = [HSUUserDefault getUserDefaultObject:kUserName];
    BOOL isRemember = [[HSUUserDefault getUserDefaultObject:kRememberPassword] boolValue];
    self.rememberSwitch.on = isRemember;
    if (isRemember) {
        self.psdTextField.text = [HSUUserDefault getUserDefaultObject:kUserPassword];
    }
    BOOL isAutoLogin = self.autoLoginSwitch.on;
    if (isAutoLogin) {
          [self loginBtnClick];
    }
}
复制代码
  • 登陆成功回调方法里 保存数据 切换程序根控制器
- (void)loginSuccess {
    [HSUUserDefault saveUserDefaultObject:self.userNameTextField.text key:kUserName];
    BOOL isAutoLogin = self.autoLoginSwitch.on;
    if (isAutoLogin) {
        [HSUUserDefault saveUserDefaultObject:@(YES) key:kAutoLogin];
        [HSUUserDefault saveUserDefaultObject:@(YES) key:kRememberPassword];
        [HSUUserDefault saveUserDefaultObject:self.psdTextField.text key:kUserPassword];
    } else { // 不自动登陆
        [HSUUserDefault saveUserDefaultObject:@(NO) key:kAutoLogin];

      BOOL isRememberPsd = self.rememberSwitch.on;
    if (isRememberPsd) { // 记住密码
        [HSUUserDefault saveUserDefaultObject:@(YES) key:kRememberPassword];
        [HSUUserDefault saveUserDefaultObject:self.psdTextField.text key:kUserPassword];
    } else {
        [HSUUserDefault saveUserDefaultObject:@(NO) key:kRememberPassword];
        [HSUUserDefault saveUserDefaultObject:nil key:kUserPassword];
    }
    }

    
    // 切换AppDelegate的控制器
    HSUTabBarController *tabBarController = [[HSUTabBarController alloc] init];
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    appDelegate.tabBarCtl = tabBarController;
    appDelegate.window.rootViewController = appDelegate.tabBarCtl;
}
复制代码
  • 在注销帐号的方法中 自动登陆设为NO 切换程序根控制器
[HSUUserDefault saveUserDefaultObject:@(NO) key:kAutoLogin];
        
        AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
        appDelegate.tabBarCtl = nil;
        LoginVC *loginVC = [[LoginVC alloc] init];
        appDelegate.window.rootViewController = loginVC;
复制代码

第二种方法

  • 在appDelegate.m中 根据是否自动登陆 设置程序根控制器
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    
    BOOL isAutoLogin = [[HSUUserDefault getUserDefaultObject:kAutoLogin] boolValue];
    if (isAutoLogin) { // 自动登陆 进入主界面
        self.tabBarCtl = [[HSUTabBarController alloc] init];
        self.window.rootViewController = self.tabBarCtl;
    } else { // 进入登陆界面
        LoginVC *vc = [[LoginVC alloc] init];
        self.window.rootViewController = vc;
    }
    
    [self.window makeKeyAndVisible];
    return YES;
}
复制代码

先讲到这,这样看第二种方法是种伪自动登陆,固然能够在AppDelegate.m中发起登陆请求,可是会怪怪的。github

// AppDelegate.m
 if (isAutoLogin) { // 自动登陆 进入主界面
        self.tabBarCtl = [[HSUTabBarController alloc] init];
        self.window.rootViewController = self.tabBarCtl;
        [self.tabBarCtl autoLogin];
    }

// HSUTabBarController.m中 实现API代理方法
- (void)loginSuccess {
    // 这里就不须要设置自动登陆什么的了
    // 处理返回的数据
}

- (void)loginFailure {// 密码错误什么的  
    // 设置自动登陆和记住密码为NO 密码为nil
    // AlertCtl显示按钮 程序根控制器置回登陆控制器
}
复制代码

总结

  • 第一种实质上每次都会调用登陆API,而第二种正规的作法则要根据token来判断发起什么请求。

2018.7.25更新

接下来了解一下token。由于要用到后台数据,无demo演示。

token是登陆令牌,是用来判断当前用户的登陆状态!算法

画张图举个例子 了解token的实现。

正规流程

当用户从设备A登陆后,服务器经过某算法生成一个token,假设为1(其实是很长的字符串),保存在数据库中而且返回这个值给A。A收到后,记录起来,今后发起(有须要此token的网络请求)都要带上这个token。若是用户从设备B登陆,那么服务器会生成新的token(假设2),(不支持多设备登陆的状况下)旧的token“1”废弃。若是A这时发起请求,服务器验证token,则可能会告诉A“此帐号在别处登陆”。数据库

再具体了解token。

  • token具备时效性。 这个时效性是后台根据具体须要设置的。 像聊天的APP,时效性能够长达1年(猜想)。 像支付的APP,时效性就应该短点。 过时后,服务器接收到请求发现该token已失效,就告诉“发起端长时间未操做,请从新登陆”。
  • token同时间内能够不惟1、拓展 考虑这种状况,某视频会员帐号最多支持三个端登陆。这时A、B、C能同时观看视频。若是这三个token还在有效期内,D这时登陆,可能没法登陆,或者顶掉一个在线设备(看后台怎么处理)。数据库的状态应该是存放有效的三个token。 QQ这种手机端、电脑端原理差很少。
  • token状态的改变 在别处登陆、帐号密码修改、过时,都会致使原token失败。这时候应该提醒用户,清空保存的token并退回登陆界面。

使用token

步骤一 token的保存获取删除

对比如下两种方法。 在安全性来讲,二者都能在未越狱手机直接导出。 在方便性来讲,UserDefault简单点。 cookie好的地方就是能设置过时时间,能本地直接判断身份信息是否过时。安全

  • 方法一 最简单的保存到NSUserDefault中
// 保存
  [userDefaults setObject:token forKey:@"token"];
  [userDefaults synchronize];
  // 获取
  [userDefaults objectForKey:@"token"];
  // 删除
  [userDefaults removeObjectForKey:@"token"];
复制代码
  • 方法二 使用cookie cookie —— 储存在用户本地终端上的数据。 其实就是字典生成cookie的文件保存到App的包里。 若是App还接有其余WebView自动登陆也是用这方法。

苹果已经帮咱们封装好了,用到两个类。 NSHTTPCookie:将字典转成可识别cookie NSHTTPCookieStorage:存储NSHTTPCookie的对象 cookie要设置(本地的)过时时间,否则App关闭就会清除!bash

建议封装一个工具类用。 HSUCookieTool.h服务器

#import <Foundation/Foundation.h>

@interface HSUCookieTool : NSObject

/**
 生成cookie

 @param name cookie的名字
 @param value cookie的值
 @param domain 域名
 */
+ (void)saveCookieWithName:(NSString *)name value:(NSString *)value domain:(NSString *)domain;


/**
 删除cookie

 @param name cookie的名字
 */
+ (void)deleteCookieWithName:(NSString *)name;


/**
 获取cookie

 @param name cookie的名字
 @return 对应的cookie,可能为空
 */
+ (NSHTTPCookie *)cookieWithName:(NSString *)name;

@end
复制代码

HSUCookieTool.mcookie

#import "HSUCookieTool.h"

@implementation HSUCookieTool

+ (void)saveCookieWithName:(NSString *)name value:(NSString *)value domain:(NSString *)domain{
    // 保存
    NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
    // 给cookie取名
    [cookieProperties setObject:name  forKey:NSHTTPCookieName];
    // 设置值
    [cookieProperties setObject:value forKey:NSHTTPCookieValue];
    // 存放目录 一般@"/"
    [cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];
    // 设置本地过时时间 一年后
    // 不设置关掉App就会清空
    [cookieProperties setValue:[NSDate dateWithTimeIntervalSinceNow:3600*24*30*12] forKey:NSHTTPCookieExpires];
    // 设置域名
    [cookieProperties setObject:[NSURL URLWithString:domain].host forKey:NSHTTPCookieDomain];
    // 生成cookie
    NSHTTPCookie *httpCookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
    // 存入仓库
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:httpCookie];
}

+ (void)deleteCookieWithName:(NSString *)name {
    NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in [cookieJar cookies]) {
        NSLog(@"cookie%@", cookie);
        if ([cookie.name isEqualToString:name]) {
            [cookieJar deleteCookie:cookie];
        }
    }
}

+ (NSHTTPCookie *)cookieWithName:(NSString *)name {
    NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in [cookieJar cookies]) {
        NSLog(@"cookie%@", cookie);
        if ([cookie.name isEqualToString:name]) {
            return cookie;
        }
    }
    return nil;
}


@end
复制代码

注意的是,相同路径下再次保存相同cookie名字会替换掉以前的同名cookie。但仍是建议先删除再添加token。网络

步骤二 请求中携带token

API代理方法应该能设置请求头 在各API中设置请求头

- (AFHTTPRequestSerializer <AFURLRequestSerialization> *)requestSerializer {
  // 获取本地的token
  NSHTTPCookie *cookie = [HSUCookieTool cookieWithName:@"token"];
  NSString *token = cookie.value;
  // 假设后台规定@"AuthorisedToken"
  AFHTTPRequestSerializer *requestSer = [AFHTTPRequestSerializer serializer];
    [requestSer setValue:token forHTTPHeaderField:@"AuthorisedToken"];
  return requestSer;
}

复制代码

最后来了解本地信息加密

研究完token发现,本地不须要记住密码也能实现自动登陆。WX也是这样。接口能够靠帐号和token实现,没密码什么事,毕竟不在本地保存密码比任何加密都来得安全。 但若是后台没有实现token,又要实现自动登陆,就只能每次调用登陆接口。这就不可避免要用到密码。

前面说过了NSUserDefaults和NSHTTPCookie,没越狱的手机能直接导出信息,让人没安全感。因此加密仍是有须要的。

  • 方法一 经过Keychain保存密码 iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每一个ios程序都有一个独立的keychain存储。 可是删除App后还存在,感受不太好。 实质就是换个安全的地方把密码保存起来。其使用很是容易,且有很多封装好的工具库。这里就不展开了。

  • 方法二 使用算法加密 苹果自带的加密算法有不少,这里介绍Base64加密。

用NSString+encrypt实现

//给定一个字符串,对该字符串进行Base64编码,而后返回编码后的结果
- (NSString *)base64EncodeString {
        //先把字符串转换为二进制数据
        NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
        //对二进制数据进行base64编码,返回编码后的字符串
        return [data base64EncodedStringWithOptions:0];
}

//对base64编码后的字符串进行解码
- (NSString *)base64DecodeString {
        //1.将base64编码后的字符串『解码』为二进制数据
        NSData *data = [[NSData alloc]initWithBase64EncodedString:self options:0];
        //2.把二进制数据转换为字符串返回
        return [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
}
复制代码

更安全的作法

和后台一块儿规定一个加密算法,对网络请求的密码参数进行加密。。到了服务器后再解密。这样即便抓包密码也能不直接暴露出来。这就须要和后台商量好了。


总结

  • 我的认为普通App(安全性要求不高)最优自动登陆方法 1.AppDelegate中判断好加载主功能界面仍是登陆界面。 2.登陆成功后要设置自动登陆,用base64加密方法加密密码和token后保存起来。 3.处理好任何有关地方的业务逻辑。

  • 业务逻辑 1⃣️保存帐号 2⃣️是否记住密码 3⃣️保存密码 4⃣️是否自动登陆 5⃣️保存token

    登陆成功一定1⃣️5⃣️。若选择(记住密码和自动登陆),2⃣️3⃣️4⃣️。 被顶号或注销帐号要❌4⃣️, ❌5⃣️。 密码错误(忽然被修改), ❌2⃣️, ❌3⃣️, ❌4⃣️, ❌5⃣️。 token过时, ❌4⃣️, ❌5⃣️。 有token更新的地方记得更改本地token。

疑惑的地方

  • 经过更改window.rootViewController 来切换登陆控制器和主功能控制器是否不妥?之前作的项目是present出来的。但网上参考的文章是这样。
  • 保存token用cookie是否是有点小题大作?比起NSUserDefaults,优点只有一个本地过时属性。
相关文章
相关标签/搜索