我们将基于以下几个方面对黑马点评项目进行分析学习:
<基于session的用户登录流程>
1)发送短信验证码
首先要对用户提交的手机号进行验证合法性,合法了,才会生成一个验证码保存在session中,接着,才会发送验证码到用户手机中
2)短信验证码登录、注册
用户手机收到验证码后输入,系统会校验这个验证码是否正确,判断正确后,还需要查询这个手机号是否存在于数据库中,也就是看看这个用户是否是首次登录。如果数据库中存在该用户,则将该用户保存到session中,如果是首次登录,则添加这个手机号的用户到数据库中,然后才将用户保存到session中
3)校验登录状态
用户登录成功后,之后的操作都会涉及到登录状态的验证,起初,第一次验证时,由于每次发起的请求中包含cookie,而cookie中携带了sessionId,系统会根据sessionId获取到session对象,然后看看session中是否包存在用户,存在时,不会立刻放行,而是把用户保存在ThreadLocal(相当于缓存)中,方便之后登录校验,可以直接从threadLocal中取
Tip:关于ThreadLocal的详解,后面会出一期进行详解,现在我们只需要把它当成是一种缓存
<为什么在第二步:短信验证码登录、注册的地方还要重复第一步验证手机号的操作?>
<使用反向的if条件判断,可以减少if语句的嵌套>
<为什么要使用拦截器?>
在登录验证中,使用拦截器,使得每个请求都需要经过拦截器,满足要求(seesion(ThreadLocal)中存在用户)才可以放行
<保存用户信息到session的粒度问题>
这样有什么坏处:占用内存、没有隐藏掉用户敏感信息(密码等)
解决方法:
1)新建一个UserDto对象,将我们想要保存在session中user的相关属性定义到UserDto中
2)修改业务代码
<集群的session共享问题>
多台tomcat中的session不能实现共享,导致请求切换到不同tomcat服务时出现数据丢失。
解决方案->用redis替代session
为什么redis可以替代session?
<用redis代替session时需要注意的问题>
1)用redis保存验证码时选择什么数据类型,key保存什么?
数据类型应该采用String
用session保存验证码时,由于每个session会对应不同的sessionid,所以多个session中使用相同的key(我们前面是用code)是可以被区分的,但是,用redis来保存验证码,就不能简单用code了,我们这里用手机号才可以区分开不同的用户
2)保存用户到redis,用什么数据结构呢?
那么,如何保证请求携带了Token呢?
这个跟前端代码有关:
Token会被保存在session中(这里也解释了为什么保存用户对象到redis时,key不能是手机号,因为这样做后面会将手机号保存在浏览器,不安全)
拦截器会对每个请求,将session中的Token放入请求头
3)用户保存在session时,每次发送请求浏览器都会更新session的过期时间,但是现在用户保存在redis中,我们如何达到这种效果呢?->用拦截器
用户每次发送请求都会经过拦截器,这个时候如果redis中保存有用户,就可以更新保存用户对象的键值对的有效期
具体步骤:
a.在MvcConfig配置类中注入RedisTemplate对象
b.在登录拦截器中创建带有StringRedisTemplate参数的构造函数
Tip:没有办法在LoginInterceptor中注入StringRedisTemplate因为这个类不是由Spring管理的
4)登录拦截器还可以做说明优化?
之前的拦截器:
只对需要登录的路径进行拦截,但是如果长期访问一些不需要登录的路径,这个时候就无法刷新token的有效期
优化后:
再在前面加一个拦截器,将实际的拦截操作放在了后一个拦截器,前一个拦截器对所有路径都拦截,如果发现用户存在就保存到ThreadLocal中并刷新token有效期,后一个拦截器只对需要登录的路径进行拦截,然后查看ThreadLocal中是否有用户存在,不存在则进行登录拦截