我学习了黑马程序员的redis课程,做点笔记供大家和我自己参考。下图所有的redis实战系列都是基于的黑马点评项目。
一、短信登录
1.导入黑马点评项目
本项目没有微服务架构,是一个前后端分离的单体式项目,前端部署在nignx上,后端项目在idea中打开即可,虽然是单体项目但是我们需要考虑它的水平扩展能力,即项目部署在tomcat上后,单台tomcat扛不住压力,能够形成一个集群,在多台tomcat上部署项目。
项目文件大家可以通过下列网盘链接进行下载
链接:https://pan.baidu.com/s/18GcAvgwhn8VoJtI8B7_6Nw?pwd=0pfe
提取码:0pfe
导入sql文件简单就不赘述了。
(1)导入后端项目
修改mysql和redis信息
运行,在浏览器中输入localhost:8081/shop-type/list,看看是否成功。
(2)导入前端项目
前端已经部署在nignx上面,所以导入比较简单,只需在cmd窗口输入start nginx.exe。
在浏览器中打开看看是否成功:输入黑马点评
到这项目就导入完毕了。
2.基于Session实现短信登录
实现短信登录流程为:发送短信验证码,短信验证码登录、注册,登录后请求需要校验登录状态
(1)发送短信验证码
根据前端发送验证码的请求,找到对应的接口类UserController
IUserInfoService类,补充这个方法
根据上图流程,在 IUserInfoServiceImp类中写出下图代码
在网页中输入手机号码,点击发送验证码,可以在idea的控制台中看到验证码
(2)短信验证码登录、注册
根据前端登录的请求,找到对应的接口类UserController
IUserService类,补充这个方法
根据上图流程,在 IUserInfoServiceImp类中写出下图代码
然后在浏览器中尝试登陆,验证一下是否成功。
我们可以发现登录后,跳转到首页,但是如果点击到我的页面就会跳到登录界面(未授权),这就需要校验登录了。
(3)校验登录状态
随着业务的完善,除了 UserController类中的login需要校验登录,还会有OrderController等其他Controller类需要校验登录,我们可以使用springmvc中的拦截器,就不用在Controller类中补充校验登录了。然而,这些Controller类可能需要用户信息,我们需要将拦截器中拦截得到的信息传递到Controller类中,在传递过程中需要注意多线程并发修改的安全问题。这个问题可用ThreadLocal(线程序对象)解决。每一个进入tomcat的请求都是一个独立的线程,ThreadLocal为每一个线程开辟一个独立的空间用来保存用户信息,这样每个线程互不干扰。用户访问不同的Controller或不同用户访问同一Controller,都有自己的用户信息。
①编写拦截器
再utils文件下创建一个loginInterceptor类,编写上图流程的代码
②配置拦截器
编写config类MvcConfig
③在对应的controller类UserController中编写放行后的代码
(4)隐藏用户敏感信息
我们可以发现,返回的用户信息太多了,而且这些信息保存在session中,会加大session的压力。
应该只保留重要的数据,我们使用已定义好的UserDTO实体类替换原来的User类。
重新登陆验证一下是否成功。
3.基于Redis实现共享session登录
(1)session共享的问题
多台tomcat并不共享session存储空间,当请求切换到不同tomcat服务时导致数据丢失的问题。
在实际情况中,为了应对并发,需要水平扩展,部署多台tomcat形成负载均衡的集群。当nginx接收的请求时,它会进行负载均衡,对所有tomcat轮询,选择一台tomcat。每个tomcat都有自己的session空间,用户信息被保存在被选择的这台tomcat中,但是当nginx接收到另一个请求时,它可能会选择另一台tomcat,这台tomcat没有保存用户信息,会认为用户当前没有登录。
tomcat之前考虑过通过拷贝让session共享:tomcat通过配置让他们之间互相可以实现一个数据拷贝。然而,多台tomcat保存相同的数据,会造成内存浪费,而且拷贝会有一定的延迟,延迟之内访问可能会导致数据不一致。
有什么东西可以满足数据共享,内存存储,key、value结构这三个条件?
redis是tomcat之外的,每个tomcat都能访问redis,便可实现数据共享;session是基于内存的,读写效率高,读写性能比较差的话难以满足高并发的需求,redis也是内存存储;redis也是key、value结构的。所以,可以用redis代替session。
(2)redis代替session
使用redis代替session,保存数据需考虑好key和value。
①验证码要保存在redis中
验证码只有6个数字,所以value可以用string类型。
key要考虑唯一性和用户可以携带这个key来读取数据。不能像session使用code作为key,因为浏览器发送请求时都有一个独立的session(在tomcat中维护了很多的session),而redis是一个共享的空间,使用code会被其他请求的验证码覆盖,考虑到唯一性,我们可以使用电话号码作为key。同时,后面获取验证码时,使用session不用考虑取数据的问题,因为浏览器第一次请求,tomcat会给浏览器创建一个session,生成sessionID,写到用户浏览器的cookie中,之后每次请求都会带着cookie,带着sessionID,也就找到session,从session中取数据。而redis通过用户提交的手机号获取验证码。
② 用户要保存在redis中。
value的数据类型可以使用string,也可以使用哈希,这里我们使用hash,因为hash修改比较方便。
key这里不推荐使用phone,推荐使用随机token。登录校验时,基于session是通过cookie(请求时携带)中的session ID获得用户数据。我们使用随机token作为key,在保存完用户信息后,返回token给客户端,前端之后每次请求都携带这个token(前端具体操作看下图)
由此可知登录凭证token保存在前端浏览器中,如果用phone就会有泄露风险。
(2)redis代替session实现
①redis保存验证码
保存常量
②redis保存用户信息
保存常量
token的有效期不能是生成的30分钟后过期,应该是有30分钟没有访问后过期,而这个访问可以通过登录校验来更新。
③修改loginInterceptor函数
(3)优化拦截器
我们知道目前的拦截器拦截的是需要登录校验的请求,在访问不会拦截的页面时,会导致token的有效期不会被更新,所以我们需要优化一下。
我们可以再创建一个拦截器,负责获取token、保存、刷新oken有效期等,这是对所有请求都执行的拦截,而另一个拦截器拦截需要登录校验的请求。
在utils文件下再创建一个 RefreshTokenInterceptor类。
原本的loginInterceptor类也需要修改
然后就是配置拦截器了