iOS开发攻城狮的集散地

iOS 实现自动登录(从低级做法到高级做法)

2018-07-23  本文已影响1609人  Hsusue

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


前言

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


先不考虑加密 用UserDefault保存

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

演示自动登录

第一种low的方法

+(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中发起登录请求,但是会怪怪的。

// 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显示按钮 程序根控制器置回登录控制器
}

总结


2018.7.25更新

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

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

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

正规流程

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

再具体了解token。

使用token

步骤一 token的保存获取删除

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

  // 保存
  [userDefaults setObject:token forKey:@"token"];
  [userDefaults synchronize];
  // 获取
  [userDefaults objectForKey:@"token"];
  // 删除
  [userDefaults removeObjectForKey:@"token"];

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

建议封装一个工具类用。
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.m

#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,没越狱的手机能直接导出信息,让人没安全感。所以加密还是有需要的。

用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];
}

更安全的做法

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


总结

疑惑的地方

上一篇 下一篇

猜你喜欢

热点阅读