持续创造,加速生长!这是我参与「日新计划 10 月更文应战」的第5天,点击查看活动概况
导言
上篇:前置常识blog.csdn.net/u011018979/… 下篇:核心完成(翻开app,假如 token不过期,就运用最近一次登录的token进行接口恳求)blog.csdn.net/u011018979/…
iOS小技能: app侧退出登录处理流程blog.csdn.net/z929118967/…
I 前置常识
1.1 分布式体系下的session
session: 一种保存key-value的机制 key:
- sessionID
- token (配合签名一起运用)
会话相关的信息存储在Redis,REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储体系,是跨渠道的非关系型数据库。
Redis 一般被称为数据结构服务器,由于值(value)可所以字符串(String)、哈希(Hash)、列表(list)、调集(sets)和有序调集(sorted sets)等类型。
redis.io/download/
Redis 是一个开源的运用 ANSI C 语言编写、恪守 BSD 协议、支撑网络、可根据内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并供给多种语言的 API。
Spring Data Redis是较大的Spring数据家族的一部分,它供给了从Spring应用程序轻松配置和访问redis的功用
。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Redis可视化工具 Redis Desktop Manager
:
官网下载:redisdesktop.com/download
mac:apps.apple.com/us/app/redi…
关注公号:
iOS逆向
,找我获取资源:redis-desktop-manager-0.8.3-2550.dmg
。
1.2 服务端侧的登录处理
登录:
- openid去和数据库里的数据匹配(选用微信授权登录)
- 设置token至redis
- 设置token至cookie
有session的接口,运用AOP获取HttpRequest,进行身份验证。
注销:
- 从cookie里查询
- 铲除redis
- 铲除cookie
1.3 app侧需求
-
冷启动app时,若token不过期,就运用最近一次登录的token进行数据恳求
-
优化token的存储办法:
之前只是存储在内存,只需杀死app,从头翻开就要求从头登录。现在改为将token信息存储到本地数据库,每次翻开app运用最近一次登录获得的token。
II app侧登录流程
2.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
2.2 token信息存储注意事项
登录账号得到的token信息。最好不要作为一个独立的单利方针存储;而是将它作为单例方针的属性userInfo,这样便于切换账号存储token和其他账号信息
- 假如之前是运用独立的单利方针UserInfoModel ,为了兼容代码能够这么做
/**
登录账号得到的token信息。最好不要作为一个独立的单利方针存储;而是将它作为单例方针的属性userInfo,便于切换账号存储token和其他账号信息。
*/
+ (instancetype)shareUserInfoModel{
return [QCTSession.shareQCTSession userInfo];
}
2.3 初始化当时用户信息的机遇
- 登录成功之后在存储userInfo的地方,初始化当时用户相关的信息(比方
GetCurrentSysUser
)。
避免第一次登录的时分,运用前次的userInfo进行初始化。
NSLog(@"shareUserInfoModel: %@",UserInfoModel.shareUserInfoModel);//0x105a70930
NSLog(@"weakSelf: %@",weakSelf);//0x105eab3f0
HSSingletonM(QCTSession);
+ (void)SaveUserInfo:(UserInfoModel *)userInfo{
QCTSession.shareQCTSession.userInfo = userInfo;
// 初始化信息
[userInfo setupinitInfo];
}
- 翻开App的时分检测是否存在token,假如存在token,初始化数据并跳转至首页。
if(UserInfoModel.shareUserInfoModel.isLoginByToken){
QCTSession.shareQCTSession.isLoginby_LoginVC =NO;
[ UserInfoModel.shareUserInfoModel setupinitInfo];
[[self class] jumpHome];
return;
}
运用线程安全形式来创立同享实例,并运用条件编译#if进行ARC、MRC的适配
kunnan.blog.csdn.net/article/det…
2.4 整体思路
保存和铲除token
- 运用BGFMDB 进保存最近一次登录的token
pod 'BGFMDB', '~> 2.0.13' #2.0.9
- 切换账号的时分替换token
- 恳求接口发现token 失效的时分,回到登录界面
III AOP
Aspect Oriented Programming(面向切面编程) 经过预编译办法和运行期动态署理完成程序功用的一致维护的一种技术.
AOP专门用于处理体系中分布于各个模块(不同办法)中的穿插关注点的问题,在JavaEE应用中,常常经过AOP来处理一些具有横切性质的体系级服务,如事务办理、安全查看、缓存、方针池办理等。
应用场景比如:对有session的接口,运用AOP获取HttpRequest,进行身份验证。
Spring结构的组成结构图如下所示:
3.1 AOP完成
- 静态AOP完成: AOP结构在编译阶段对程序进行修改,即完成对方针类的增强,生成静态的AOP署理类,以AspectJ为代表。
静态AOP完成具有较好的性能,但需求运用特别的编译器。
- 动态AOP完成: AOP结构在运行阶段动态生成AOP署理,以完成对方针方针的增强,以Spring AOP为代表。
动态AOP完成是纯Java完成,因而无须特别的编译器,可是一般性能略差。
3.2 AOP的基本概念
- 切面(Aspect): 切面用于组织多个Advice,Advice放在切面中界说。
- 衔接点(Joinpoint): 程序履行过程中明确的点,如办法的调用,或者反常的抛出。在Spring AOP中,衔接点总是办法的调用。
- 增强处理(Advice): AOP结构在特定的切入点履行的增强处理。处理有”around”、”before”和”after”等类型
- 切入点(Pointcut): 能够插入增强处理的衔接点。简而言之,当某个衔接点满意指定要求时,该衔接点将被增加增强处理,该衔接点也就变成了切入点。
3.3 相关注解
@Aspect:作用是把当时类标识为一个切面,供容器读取。
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的界说包括2部分,一是表达式,二是办法签名。办法签名有必要是 public及void型。能够将Pointcut中的办法看作是一个被Advice引证的助记符,由于表达式不直观,因而咱们能够经过办法签名的办法为此表达式命名。因而Pointcut中的办法只需求办法签名,而不需求在办法体内编写实际代码。
@Around:盘绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,办法正常退出时履行
@Before:标识一个前置增强办法,相当于BeforeAdvice的功用
@AfterThrowing:反常抛出增强,相当于ThrowsAdvice
@After: final增强,不管是抛出反常或者正常退出都会履行
3.4 AOP完成身份验证
- 创立一个切面类Aspect
@Aspect
@Component
@Slf4j
public class AuthorizeAspect {
}
- 设置切入点Pointcut
public class AuthorizeAspect {
//阻拦有session的操作,扫除登陆登出的操作
@Pointcut("execution(public * com.jess.sell.controller.xxxSeller*.*(..))" +
" && !execution(public * com.jess.sell.controller.xxxxxSellerUserController.*(..))")
public void verify() {
}
}
- 设置阻拦后的操作Advice
@Before("verify()")//@Before:标识一个前置增强办法
public void doVerify() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//查询 cookie
Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN);
if (cookie == null) { //没有登录
log.warn("【登录校验】 cookie中没有token");
throw new AuthorizeException();
}
//查询 redis
String tokenValue = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue()));
if(StringUtils.isEmpty(tokenValue)){
log.warn("【登录校验】 redis中没有token");
throw new AuthorizeException();
}
}
- 反常阻拦:SpringBoot自带反常阻拦@ControllerAdvice
创立一个SellerExceptionHandler类
@ControllerAdvice
public class ExceptionHandler {
private ProjectUrlConfig projectUrlConfig;
/**
* 阻拦登录反常,并跳转到登录界面
* value = AuthorizeException.class)表示阻拦的反常为AuthorizeException反常
*/
@ExceptionHandler(value = AuthorizeException.class)
public ModelAndView handlerAuthorizeException(){
return new ModelAndView("redirect:"
.concat(projectUrlConfig.getWechatOpenAuthorize())
.concat("/sell/wechat/qrAuthorize")
.concat("?returnUrl=")
.concat(projectUrlConfig.getSell())
.concat("/sell/seller/login"));
}
}