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,優點只有一個本地過時屬性。
相關文章
相關標籤/搜索