iOS开发知识小集iOS学习开发iOS开发点滴

关于iOS开发者中APP保持登录状态的几种实现方式和一些思考

2019-10-09  本文已影响0人  小蜜蜂Bee

在实际开发中几乎所有的APP都会存在用户体系,假如没有涉及用户体系的APP,提交审核的时候有很大概率会被苹果爹地直接拒绝不允许上架。而有了用户体系,那么就肯定会存在登录以及保持登录的需求,要不然用户每次启动APP都得重新登录,那就乖乖了。保持登录状态的方式目前大致有以下几种方式:

一、利用Cookie机制实现

我们知道cookie是为了解决http无状态的一种技术,被电商、oa等web应用广泛使用。如果我们的App和后端通讯采用的http通讯方式,可以利用cookie技术进行登录状态保持。比如我们可以把sessionID和有效期保存在cookie中,发给前端App,前端App收到后保存在本地。当访问后端服务把sessionID和有效期作为参数传给后台进行认证。直到sessionID失效,用户都不需要重新登录。

二、本地保存用户名和密码

当用户第一次输入账号和密码的时候,APP端本地保存用户的账号和密码,下次启动的时候获取本地保存的账户和密码进行登录,由我们开发者在后台进行登录处理,不过这个有个不是太合适的就是相当于在APP本地保存了用户的这些信息,意义上来说不安全,不过大致的实现方式如下:
1、在登陆页面对应的类loginViewController.h中定义两个TextField和一个Button,用来接受用户输入的用户名和密码,点击按钮登陆,如果登陆成功,就将用户的登陆信息存放在UserDefault中,然后跳入主页面。

@interface LoginViewController ()
@property (nonatomic, strong) UITextField *username;
@property (nonatomic, strong) UITextField *password;
@end

2、在loginViewController.m中实现两个TextField和一个Button,直接实现按钮的点击登录事件:登录请求成功后,走成功回调,回调下面实现将用户名和密码存入UserDefault中,页面跳转到主页面。下面数据请求的代码略去了,直接上存储UserDefault代码,跳转主页面。

    NSString *username = self.username.text;
    NSString *password = self.password.text;
    
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setObject:username forKey:@"username"];
    [userDefaults setObject:password forKey:@"password"];
    [userDefaults synchronize];
    
    UITabBarController *tabBarVc = [[UITabBarController alloc] init];
    UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:tabBarVc];
    AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    appdelegate.window.rootViewController = nc;

3、在AppDelegate.m中实现:用户第一次进入APP时自动进入登录注册页,提示用户注册登录,用户登录成功后才进入主页,再次进入APP时,不用再次登录就直接进到主页了

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //初始化Window
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.backgroundColor = [UIColor whiteColor];
    
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    
    if (![userDefaults objectForKey:@"username"]){
        //进入主页
     UITabBarController *tabbarVc = [[UITabBarController alloc]init];
     UINavigationController *navVc = [[UINavigationController alloc]initWithRootViewController:tabbarVc];
    
    self.window.rootViewController = navVc;
    } else {
       //进入登录页面
        LoginViewController *loginVc = [[LoginViewController alloc] init];
        [self.navController pushViewController:loginVc animated:YES];
    }
    
    [self.window makeKeyWindow];
    return YES;
}

4、退出登录

//退出登录,清除用户信息
- (void)logout{
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults removeObjectForKey:@"username"];
    [userDefaults removeObjectForKey:@"password"];
    
    //跳转登陆页
    LoginViewController *loginVc = [[LoginViewController alloc] init];
    [self.navController pushViewController:loginVc animated:YES];
}

三、前后端配合采用token方式

token方式在app认证上用的比较普遍,App初始登录时,提交账号和密码数据给服务端,服务端根据定义的的策略生成一个token字符串,token字符串中可以包含用户信息、设备ID等信息以保证用户的唯一性,服务端并对token设置一定的期限。服务端把生成的token字符串传给客户端,客户端保存token字符串,并在接下来的请求中带上这个字符串。相对于在App本地token的安全性更高了。
说明:这里说说关于设备唯一ID的小细节,如果直接把设备唯一ID传给后台的话,这里可能会出现一个问题,比如在同一台手机退出登录后再登录其他账号,那么就会出现多个账号的设备ID就都一样了。这样会带来什么后果呢?比如如果还使用极光推送的话,用这个设备ID注册别名的话,那么就会出现多个账号的别名都是一样的,那么推送就乱了。

//uuid + 手机  + iOS
    NSString *uuid = [[GetUUID getUUID] stringByReplacingOccurrencesOfString:@"-" withString:@""];
    
    //截取UUID的后十位
    NSString *uuidPart = [uuid substringFromIndex:uuid.length - 10];
    
    NSString *aliasStr = [NSString stringWithFormat:@"%@_%@_iOS",uuidPart,phoneNum];

上面就是我在实际开发使用设备ID唯一性的处理,就是UUID + 手机号码 + iOS(平台),安卓就是UUID + 手机号码 + Android。这样子就不仅避免了同一台设备登录多个账号的问题,还区分了不同平台登录的账号。
1、用户第一次登录之后本地保存token

[[NSUserDefaults standardUserDefaults] setObject:[responseObject.data objectForKey:@"token"] forKey:token];
[[NSUserDefaults standardUserDefaults] synchronize];

2、再次启动的时候获取本地保存的token

NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:token];

然后用这个token请求一个后台验证该token是否在有效期的接口,接着根据后台返回的信息进行相关页面的跳转处理。

到此为止,目前几种保持登录状态的方法大致介绍完了,不知道细心的你有没有发现一个小细节,那就是很多都是采用根据登录状态是否有效来设置window的rootViewController根控制器,就是说没有登录状态下rootViewController是loginViewController登录页,有登录状态有效的情况下rootViewController是navVC导航控制器或者其他UIViewController控制器等,如下:

if(有登录状态){

    BaseTabBarViewController *baseVc = [[BaseTabBarViewController alloc] init];
   self.window.rootViewController = baseVc;

}else{

    LoginViewController *loginVc = [[LoginViewController alloc] init];
    self.window.rootViewController = loginVc;

}

但是有一种比较特殊的情况,其实也不特殊,就是也是一种产品形式,那就是不管有没有登录状态下,window的rootViewController都是BaseTabBarViewController,就是说不会随着登录状态失效根控制器进行更换,因为很多APP也都存在游客模式,就是不用登录状态也可以进入APP进行基础的操作,而当点击到需要登陆地方时没有登录状态的话就跳转到登录页,有的话就继续下一步操作,那么这个时候我们应该怎么处理呢?下面继续说说暂时个人想到的处理方式:

处理方式1:

首先设置window的rootViewController根控制器为BaseViewController(UIViewController类型的),然后再立即获取用户的登录状态是否有效,不管登录状态是否有效都会把window的rootViewController设置为BaseTabBarViewController,只是在获取到用户登录状态信息之后BaseTabBarViewController里的有些UI展示不一样,直接上代码:


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  
    //token登录
    [self initService];
    
    
    //先创建根控制器,然后会在AppDelegate+AppService分类中进行根控制器的替换
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    //先进行网络监测防止手机开启飞行模式时进入白屏
    if (![XMFUserHttpHelper checkNetStatus]) {
        
        XMFTabBarViewController *baseVc = [[XMFTabBarViewController alloc] init];
        self.window.rootViewController = baseVc;
        
    }else{
        
        XMFBaseViewController *baseVc = [[XMFBaseViewController alloc] init];
        self.window.rootViewController = baseVc;
    }
    
    [self.window makeKeyAndVisible];
}
//初始化服务
-(void)initService{
   
    NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:token];
    
    
    if (![sessionId isBlankString]) {//先判断是否是空字符串
        
        //获取保存的token供下面接口的头部使用
        [XMFGlobleManager getGlobleManager].token = token;

        [SVProgressHUD show];
        
        [[XMFUserHttpHelper sharedManager] sessionLoginCheckWithParam:nil success:^(XMFResponseModel *responseObject) {
            
             [SVProgressHUD dismiss];
      
            //token在有效期内
            if (responseObject.returnCode == XMFHttpReturnCodeSuccess) {
        token = [responseObject.data objectForKey:@"token"];
                [XMFGlobleManager getGlobleManager].isLogin = YES;
                
                //发送登录成功的通知
       [[NSNotificationCenter defaultCenter] postNotificationName:DWQ_NOTIFY_LoginState_Change object:@YES userInfo:nil];
                
         
                [[NSUserDefaults standardUserDefaults] setObject:[responseObject.data objectForKey:@"token"] forKey:token];
                
                [[NSUserDefaults standardUserDefaults] synchronize];
                
                 [self setRootViewController];
                
            }else{
                
                [self setRootViewController];
                

                //登录失败
                [[NSNotificationCenter defaultCenter] postNotificationName:DWQ_NOTIFY_LoginState_Change object:@NO userInfo:nil];
            }
            
        } failure:^(NSError *error) {
            
            [self setRootViewController];
            
        }];
        
    }else{
        
         [self setRootViewController];
    }
    
    
}


//等待验证token是否有效之后进行设置根控制器
-(void)setRootViewController{
 
    
    XMFTabBarViewController *tabvc = [[XMFTabBarViewController alloc] init];
    self.window.rootViewController = tabvc;
    
    
}

这样子大致就把不管有无登录状态rootViewController都为XMFTabBarViewController的情况处理好了,不过这里面还有一个小细节会影响体验,仔细看看上面是不是登录状态是否有效取决于后台返回,网络请求快的情况下可能感受不到,那万一网络请求缓慢的话,那就会出现用户会在等待AFHTTPSessionManager的manager.requestSerializer.timeoutInterval = 20;时间之后才出现了XMFTabBarViewController,这样的体验是有点影响的,那么再来继续优化一下。

处理方式2:

这里直接不管是否有登录状态都直接设置window的rootViewController为XMFTabBarViewController,代码如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    //token登录
    [self initService];

    //先创建根控制器,然后会在AppDelegate+AppService分类中进行根控制器的替换
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    ZFTabBarViewController *baseVc = [[ZFTabBarViewController alloc] init];
    self.window.rootViewController = baseVc;
    
    [self.window makeKeyAndVisible];
}
//初始化服务
-(void)initService{
   
    NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:token];
    
    
    if (![sessionId isBlankString]) {//先判断是否是空字符串
        
        //获取保存的token供下面接口的头部使用
        [XMFGlobleManager getGlobleManager].token = token;

        [SVProgressHUD show];
        
        [[XMFUserHttpHelper sharedManager] sessionLoginCheckWithParam:nil success:^(XMFResponseModel *responseObject) {
            
             [SVProgressHUD dismiss];
      
            //token在有效期内
            if (responseObject.returnCode == XMFHttpReturnCodeSuccess) {
        token = [responseObject.data objectForKey:@"token"];
                [XMFGlobleManager getGlobleManager].isLogin = YES;
                
                //发送登录成功的通知
       [[NSNotificationCenter defaultCenter] postNotificationName:DWQ_NOTIFY_LoginState_Change object:@YES userInfo:nil];
                
         
                [[NSUserDefaults standardUserDefaults] setObject:[responseObject.data objectForKey:@"token"] forKey:token];
                
                [[NSUserDefaults standardUserDefaults] synchronize];
  
            }else{

                //登录失败
                [[NSNotificationCenter defaultCenter] postNotificationName:DWQ_NOTIFY_LoginState_Change object:@NO userInfo:nil];
            }
            
        } failure:^(NSError *error) {

        }];
        
    }
    
}

然后在会随着登录状态改变UI界面的页面进行对“isLogin”属性进行kvo监测,接着进行相应的UI改变,代码如下:

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];

    //增加对登录状态"isLogin"值变化的观察
    kWeakSelf(self)
    
    [JJKeyValueObserver addObserveObject:[XMFGlobleManager getGlobleManager] keyPath:@"isLogin" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew changeBlock:^(NSDictionary *dict) {
        
//        DLog(@"登录状态发生了改变:%@",[dict description]);
        
       weakself.rightLoginBtn.selected = [XMFGlobleManager getGlobleManager].isLogin;
        
        
    }];
}

同时博主的kvo使用的是第三方JJKeyValueObserver,大家有需要的可以去GitHub上下载使用,也可以自己实现kvo,原理是一样的。那经过这么一番处理之后,整个保持登录的过程差不多就处理完了,同时博主开发中采用的是“前后端配合采用token方式”。

如果以上的分享帮助到你了,欢迎分享,更欢迎赞赏,也可以直接打开支付宝、微信、QQ的扫一扫功能直接扫下面的支付宝、微信、QQ三合一打赏码进行打赏支持作者创作,感谢感谢!

image

欢迎和我交流,QQ:834537795(小蜜蜂)

上一篇下一篇

猜你喜欢

热点阅读