最近在重構App。舊的版本登陸模塊沒有自動登陸功能,體驗極其很差。網上搜索了不少教程都沒找到完整的,故寫篇文章梳理一下。ios
Demo連接 https://github.com/Hsusue/iOS-AutoLogin 該Demo基於第二種方法,包含了界面邏輯、加密演示、token工具包。 第一種差很少且比較簡單,如下會貼出代碼。 git
+(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];
}
複製代碼
- (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;
}
複製代碼
- (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;
}
複製代碼
[HSUUserDefault saveUserDefaultObject:@(NO) key:kAutoLogin];
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
appDelegate.tabBarCtl = nil;
LoginVC *loginVC = [[LoginVC alloc] init];
appDelegate.window.rootViewController = loginVC;
複製代碼
- (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顯示按鈕 程序根控制器置回登陸控制器
}
複製代碼
token是登陸令牌,是用來判斷當前用戶的登陸狀態!算法
當用戶從設備A登陸後,服務器經過某算法生成一個token,假設爲1(其實是很長的字符串),保存在數據庫中而且返回這個值給A。A收到後,記錄起來,今後發起(有須要此token的網絡請求)都要帶上這個token。若是用戶從設備B登陸,那麼服務器會生成新的token(假設2),(不支持多設備登陸的狀況下)舊的token「1」廢棄。若是A這時發起請求,服務器驗證token,則可能會告訴A「此帳號在別處登陸」。數據庫
對比如下兩種方法。 在安全性來講,二者都能在未越獄手機直接導出。 在方便性來講,UserDefault簡單點。 cookie好的地方就是能設置過時時間,能本地直接判斷身份信息是否過時。安全
// 保存
[userDefaults setObject:token forKey:@"token"];
[userDefaults synchronize];
// 獲取
[userDefaults objectForKey:@"token"];
// 刪除
[userDefaults removeObjectForKey:@"token"];
複製代碼
蘋果已經幫咱們封裝好了,用到兩個類。 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。網絡
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。