美文网首页iOS开发知识小集iOS学习开发iOS开发点滴
关于iOS开发者中APP保持登录状态的几种实现方式和一些思考

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

作者: 小蜜蜂Bee | 来源:发表于2019-10-09 15:50 被阅读0次

    在实际开发中几乎所有的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(小蜜蜂)

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/ygqmyctx.html