- 1.1 开发步骤
- 1.2 token信息存储注意事项
- 3.1 Session 对象的创建
- 3.2 保存token到UserInfoModel 对象中的时机
- 3.3 再次打开app的时候获取token
- 3.4 退出登录或者(token)失效进行信息信息清除
- 3.5 在登录界面的viewDidLoad 进行判断是否直接进入首页
- 3.6 token存储区分正式环境和测试环境
- 3.7 注意事项
- 单点登录(Single Sign On)
前言
需求:
1、冷启动app时,若token不过期,就使用最近一次登录的token进行数据请求
2、优化token的存储方式:之前只是存储在内存,只要杀死app,重新打开就要求重新登录。现在改为将token信息存储到本地数据库,每次打开app使用最近一次登录获得的token。
3、 调试技巧 如果BGFMDB的表新增字段,可更换一下表名,避免找不到字段,数据更新失败
4、原文 https://kunnan.blog.csdn.net/article/details/105202605
5、付费文章:iOS App登录流程优化:如果 token不过期,就使用最近一次登录的token【修订版】
本文限时免费
I 、 步骤
1.1 开发步骤
- I、保存token到UserInfoModel 对象中
- II、再次打开app的时候获取token
- III、退出登录或者(token)失效进行信息信息清除
- IV、在登录界面的viewDidLoad 进行判断是否直接进入首页
- V :token存储区分正式环境和测试环境(UserInfoModel 对象新增一个当前token的域名属性currentHost,用于查询判断)
5.1) 更换表名
5.2)UserInfoModel新增字段currentHost,
5.3)查询token新增条件currentHost
5.4) 存储token新增字段currentHost
1.2 token信息存储注意事项
登录账号得到的token信息。最好不要作为一个独立的单利对象存储;而是将它作为单例对象的属性userInfo,这样便于切换账号存储token和其他账号信息
- 如果之前是使用独立的单利对象UserInfoModel ,为了兼容代码可以这么做
/** 登录账号得到的token信息。最好不要作为一个独立的单利对象存储;而是将它作为单例对象的属性userInfo,便于切换账号存储token和其他账号信息。 */ + (instancetype)shareUserInfoModel{ return [QCTSession.shareQCTSession userInfo]; }
使用线程安全模式来创建共享实例,并使用条件编译#if进行ARC、MRC的适配
II、 整体思路
保存和清除token
- 使用BGFMDB 进保存最近一次登录的token
pod 'BGFMDB', '~> 2.0.13' #2.0.9
- 切换账号的时候更换token
- 请求接口发现token 失效的时候,回到登录界面
III、核心代码
3.1 Session 对象的创建
使用HSSingleton工具类实现单例,并将包含登陆token及其他有状态相关字段的userInfo对象作为单例对象的属性
HSSingletonH(Session); HSSingletonM(Session); + (void)SaveUserInfo:(UserInfoModel *)userInfo{ QCTSession.shareQCTSession.userInfo = userInfo; }
存储token信息
#pragma mark - ******** iOS 优化登录流程:【打开app,如果 token不过期,就使用最近一次登录的tokenn进行接口请求。】(现状是每次打开app都会重新登录) + (void)saveModelWithModel:(UserInfoModel*)userModel{ [self emptySeeionLocal]; userModel.bg_tableName = QCTUserInfoModelTableName; BOOL isSave = [userModel bg_save];// 保存方法 if (isSave) { NSLog(@"token保存成功:%@",userModel.mj_keyValues); }else{ NSLog(@"token保存失败:%@",userModel.mj_keyValues); } } +(void)emptySeeionLocal{ NSMutableArray *tmparr = [NSMutableArray arrayWithArray:[[self class] bg_find:QCTUserInfoModelTableName where:nil] ]; // 先删除 for (UserInfoModel *loginModel in tmparr) { NSString *where = [NSString stringWithFormat:@"where %@=%@",bg_sqlKey(@"bg_id"),bg_sqlValue(loginModel.bg_id)]; BOOL isDelete = [UserInfoModel bg_delete:[NSString stringWithFormat:@"%@",QCTUserInfoModelTableName] where:where]; NSLog(@"删除重复数据%@:%@",isDelete?@"成功":@"失败",loginModel.mj_keyValues); // break; // } }
获取token信息
+ (instancetype)getmodel4LoginSeesion{ NSMutableArray *tmparr = [NSMutableArray arrayWithArray:[[self class] bg_find:QCTUserInfoModelTableName where:nil] ]; if(tmparr.count>0 ){ UserInfoModel *userModel = tmparr.firstObject; // QCTSession.shareQCTSession.userInfo = userModel; [ QCTSession SaveUserInfo:userModel]; NSLog(@"获取的token 信息%@",userModel); return tmparr.firstObject; } NSLog(@"获取的token userModel.shareUserInfoModel %@",UserInfoModel.shareUserInfoModel); // return UserInfoModel.shareUserInfoModel; }
3.2 保存token到UserInfoModel 对象中的时机
- 登录的时候
[ Session SaveUserInfo:userModel]; [UserInfoModel saveModelWithModel:userModel];
- app 退出的时候:applicationWillTerminate 、applicationWillResignActive
- (void)applicationWillTerminate:(UIApplication *)application { // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. [UserInfoModel saveModelWithModel:UserInfoModel.shareUserInfoModel]; }
3.3 再次打开app的时候获取token
#pragma mark - ******** 获取token信息 - (void)initInfo{ QCTSession.shareQCTSession.tmpUserInfoModel = nil; [UserInfoModel getmodel4LoginSeesion];// 获取会话 } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [self initInfo]; }
3.4 退出登录或者(token)失效进行信息信息清除
- 在 application :didFinishLaunchingWithOptions: 方法进行监听
__weak __typeof__(self) weakSelf = self; [[[NSNotificationCenter defaultCenter] rac_addObserverForName:kExitlogshowLoginViewNotification object:nil] subscribeNext:^(NSNotification *notification) { NSString *tmp = @""; if(notification.object){ tmp = notification.object; [weakSelf.window showHUDMessage:tmp afterBlock:^(id _Nonnull sender) { [weakSelf setupExitlogout]; }]; }else{ // tmp = @""; [weakSelf setupExitlogout]; } }];
- 退出登录(token过期)的处理
/** 1、移除极光的别名 2、初始化一些信息 3、清除账户信息缓存(本地数据库和内存中的token信息) */ - (void)setupExitlogout { [JPUSHService setTags:nil alias:@"" callbackSelector:@selector(tagsAliasCallback:tags:alias:) object:self]; [UserInfoModel cleanInfoWithblock:^(id sender) { //3、登录 UserInfoModel.shareUserInfoModel.token = nil; AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; [appDelegate setupLoginV]; }]; }
- 清除本地数据库的token
+ (void)cleanInfoWithblock:(void (^)(id sender))block{ [[UserInfoModel shareUserInfoModel] cleanInfo]; if(block){ block(nil); } } - (void)cleanInfo{ #pragma mark - ******** 包括清除本地数据库的token [[self class] emptySeeionLocal];// [self setToken:nil]; _CurrentSysUser = nil; // _ [QCTSession shareQCTSession].tmpUserInfoModel = nil; }
3.5 在登录界面的viewDidLoad 进行判断是否直接进入首页
- viewDidLoad 早createSubView的时候进行判断,是否直接进入首页
if(UserInfoModel.shareUserInfoModel.isLoginByToken){ [[self class] jumpHome]; return; }
- 判断是否有token
/** 判断是否有token */ - (BOOL)isLoginByToken{ if(![NSStringQCTtoll isBlankString:UserInfoModel.shareUserInfoModel.token]){ return YES; } return NO; }
3.6 token存储区分正式环境和测试环境
- UserInfoModel 对象新增一个当前token的域名属性currentHost,用于查询判断环境
- BGFMDB如果新增字段,就需要更换一下表名
#warning BGFMDB如果新增字段,就需要更换一下表名,否则在旧表会找不到字段,无法更新数据 2021-01-29 17:16:10.418453+0800 Housekeeper[15013:1071895] [logging] table QCTUserInfoModelTableName0401 has no column named BG_IsreqGetCurrentSysUsering in "insert into QCTUserInfoModelTableName0401(BG_currentHost,BG_BearerToken,BG_token,BG_DictionariesEnum,BG_DictionariesEnumDictionary,BG_loginCode,BG_isGotoChangePassword,BG_HaveDefaultLevel,BG_bg_updateTime,BG_loginMessage,BG_IsreqGetCurrentSysUsering,BG_IsTobacco,BG_bg_createTime,BG_MsgsourceStr,BG_isShowBindingMobileNote) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"
- 根据域名进行存取token的
NSString* where = [NSString stringWithFormat:@"where %@=%@",bg_sqlKey(@"currentHost"),bg_sqlValue(currentHost)]; NSMutableArray *tmparr = [NSMutableArray arrayWithArray:[[self class] bg_find:QCTUserInfoModelTableName where:where] ];
存储token信息
+ (void)saveModelWithModel:(UserInfoModel*)userModel{ [self emptySeeionLocal]; userModel.bg_tableName = QCTUserInfoModelTableName; userModel.currentHost = currentHost; BOOL isSave = [userModel bg_save];// 保存方法 if (isSave) { NSLog(@"token保存成功:%@",userModel.mj_keyValues); }else{ NSLog(@"token保存失败:%@",userModel.mj_keyValues); } }
3.7 注意事项
2020-03-30 19:23:12.877204+0800 Housekeeper[5577:1118700] DB Error: 1 "duplicate column name: BG_CompanyId"
BGFMDB 不支持大小写,因此保存的对象不能有大小写的两个字段
@property (copy, nonatomic) NSString *companyId; //@property (strong, nonatomic) NSString *CompanyId;
2020-04-01 16:18:32.127564+0800 retail[8875:1402725] [logging] table QCTUserInfoModelTableName has no column named BG_currentHost
如果新增字段,就需要更换一下表名