redis的共享session应用

项目背景:

该项目背景就是黑马的黑马点评项目。

一:基于Session实现验证码登录流程

基本的登录流程我们做了很多了。这个是短信登录流程

其实和普通的登录流程就多了一个生成验证码,并将验证码保存在session中,并且呢,返回给前端,前端登录的时候需要多提交一个表单项而言

整体流程:

整体的流程就是生成了一个验证码并且保存在session中

当用户登录时,我们就在session中取出对应的验证码然后和用户传的验证码是否相同来比对

如果用户不存在,就注册之后,将用户存储到当前的线程中

如果用户存在,直接存储到当前的线程中

这个是前端拦截器的实现


关于这个cookie和session的会话技术,中间具体的过程就是:

当登录成功之后,在tomcat服务器会生成具有唯一ID的session,前端会有一个Cookie保存在浏览器中,每次前端发送请求,就会带上这个Cookie,根据这个Cookie会匹配一个Session,我们就可以通过这个Session是否存在来知道用户是否登录

具体的在:SpringMVC(包括Servlet,会话技术)理解-CSDN博客

这里需要有一个重要的思维:就是Tomcat也是有内存的,

在一开始的时候,黑马的课是直接将整个User实体类存储到当前线程中,但User实体类属性很多,会占用更多的内存,所以后面就存储UserDTO这个类,相对实体类属性较少,会节省空间,所以我们需要对Tomcat的线程和内存的量都需要有概念,可能以后项目优化就可以从这个点下手。

Redis代替session进行短信验证登录流程

 首先为什么要用这个Redis代替传统的Session进行存储呢?

这个是关于分布式的知识:

Redis-CSDN博客这一篇有

先来看个图也行

具体是下面的五个步骤:

1:先将生成的验证码放到redis中

@Overridepublic Result sendCode(String phone, HttpSession session) {//1: 对传递的电话进行合法校验if (RegexUtils.isPhoneInvalid(phone)) {//2:不合法直接返回return Result.fail("手机号不合法");}//3:合法,生成验证码final String code = RandomUtil.randomNumbers(6);//4:将验证码保存到Redis中,且key为当前用户手机号redisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY + phone, code);//5:模拟发送验证码log.debug("发送短信验证码成功,验证码:{}", code);//6:返回return Result.ok();}

 整体的代码逻辑:

先用正则表达式来校验电话是否合法

再用Hutool的RandomUtil生成6位随机数字

最后将具有业务意义的Redis常量+用户的电话做为Key存储到redis中

最后只是模拟发送了验证码

2:login登录接口去redis中查找验证码

3:login登录接口中将登录成功之后的用户信息存储到redis中

这里我们需要先明确的一点是:

我们用Redis中的那种数据结构来存储用户信息

我们常用的String,如果用来存储的话,肯定可以不过不便于扩展

最好的数据结构是Hash

这两步在一个方法中

@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {String phone = loginForm.getPhone();String code = loginForm.getCode();//1:校验手机号if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手机号不合法");}//校验验证码// 从redis中取出验证码String cacheCode = (String) redisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + phone);if (StringUtils.isBlank(code)|| StringUtils.isBlank(cacheCode)|| !code.equals(cacheCode)) {return Result.fail("验证码不能为空或验证码错误");}//2:根据手机号查询用户User user = query().eq("phone", phone).one();//3:如果这个用户不存在,就创建新用户if (user == null) {user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));boolean save = this.save(user);if (!save) {return Result.fail("创建用户失败");}}//4:保存用户到Redis 这里要有一个内存的概念,就是你将整个User实体全都存进去,占用的内存就比UserDTO大很多//4.1:生成tokenString token = UUID.randomUUID().toString();//4.2:将User对象转为UserDTOfinal UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);//4.3:将UserDTO转换成map对象final Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(), CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));redisTemplate.opsForHash().putAll(RedisConstants.LOGIN_USER_KEY + token, userMap);//4.4:设置token的过期时间redisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);return Result.ok(token);}

一般我们会将Redis的Key中的字符串抽象成一个常量 

package com.hmdp.utils;public class RedisConstants {public static final String LOGIN_CODE_KEY = "login:code:";public static final Long LOGIN_CODE_TTL = 2L;public static final String LOGIN_USER_KEY = "login:token:";public static final Long LOGIN_USER_TTL = 36000L;
}

整体的代码逻辑:

先用正则表达式来校验电话是否合法

用相同的Key从redis中取出验证码进行校验

根据手机号查询用户,根据是否存在然后进行注册或者登录

生成一个token,这个就是redis中的key,并且将这个token返回给前端,前端请求其它接口的时候,在请求头中带上token,后端根据这个token在redis中查找

这里生成token之后,还需要将用户User(从数据库中查出来的)转成UserDTO,在转成Map

这里可以用一个Hutool的BeanUtil.beanToMap,并且呢

这里有一个问题就是,UserDTO中有一个字段是Long类型的,我们的userMap都是<String,Object>,所以我们可以用这个工具类提供的第三个参数:

CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()))

来自定义我们自己的转换规则

最后我们再设置一下过期时间,过期时间的作用肯定不用多说,非常重要。


但是我们现在这样写还是有问题:就是我们已经设置了这个redis中的session过期时间,但是,我们会发现,我们就只在这个登录接口中设置了,比如用户调用了其它的接口,这说明用户还是在我们这个应用中,不过到时间了,用户的redis中的session自动过期,直接下线,这样用户体验不好,所以,我们下一个解决策略就是在拦截器中刷新这个过期时间。

4:在拦截器中重新刷新redis的过期时间,并且将用户信息保存到ThreadLocal中

但这里我们可以同时把第五个步骤抛出来

就是:我们光在拦截器这里刷新就够了嘛?比如有些接口在这个LoginInterceptor中没有被拦截到),我们在这个LoginInterceptor之前再加一个接口,专门做redis的刷新,在下一个接口再进行拦截,有点像微服务网关后面再加一个这个MVC拦截器的流程一样

5:解决一些未被第一个拦截器拦截到的路径的接口redis过期的问题

package com.hmdp.utils;public class RefreshRedisIntercepor implements HandlerInterceptor {private RedisTemplate redisTemplate;public RefreshRedisIntercepor(RedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.3:从前端请求中取出tokenfinal String token = request.getHeader("authorization");//1.4:判断token是否为空if(StringUtils.isBlank(token)){return true;}//1.6:从redis中取出userMapfinal Map userMap = redisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY+token);//1.7 :判断userMap是否为空if (userMap.isEmpty()) {return true;}// 手动转换 id 字段if (userMap.containsKey("id")) {Object idValue = userMap.get("id");if (idValue instanceof String) {userMap.put("id", Long.valueOf((String) idValue)); // 转换为 Long}}final UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//2:保存用户的信息到ThreadLocalUserHolder.saveUser((UserDTO) userDTO);//3:刷新Redis的过期时间redisTemplate.expire(RedisConstants.LOGIN_USER_KEY + userDTO.getId(), RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//3:在请求结束后,移除ThreadLocal中的用户信息UserHolder.removeUser();}
}

这个是Redis刷新拦截器

上面的注释就是代码流程了

这里有一个小小的思维点,就是这个拦截器,叫拦截器,但是它不拦截,就是如果你的参数不合法,我不拦截,我直接给你通过,到下一个点去去拦截

package com.hmdp.utils;import com.hmdp.dto.UserDTO;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class LoginIntercepor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1:判断ThreadLocal是否有userDTOfinal UserDTO user = UserHolder.getUser();if(user == null){response.setStatus(401);return false;}return true;}
}

 这个是第二个拦截器,

这个拦截器的逻辑很简单,判断当前线程中是否有这个userDTO,如果没有,就拦截,有就放行


这里有个小技巧,

就是我们这个RefreshRedisIntercepor不是bean对象,所以不能依赖注入RedisTemplate。

我们可以在MvcConfig中进行配置:

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Autowiredprivate RedisTemplate redisTemplate;//配置拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginIntercepor()).excludePathPatterns("/user/code", "/user/login", "/shop/**", "/shop-type/**", "/upload/**", "/blog/hot", "/blog/search").order(1);registry.addInterceptor(new RefreshRedisIntercepor(redisTemplate)).order(0);//order :调整优先级}}

在RefreshRedisIntercepor留一个构造方法

在这个MvcConfig进行依赖注入

并且这里可以通过.order(0)这个方法进行优先级的排行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/413429.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《JavaEE进阶》----1.<JavaEE进阶可以学到什么>

本篇博客会讲到 一、JavaEE进阶学习内容&#xff1a; 1.框架的学习&#xff1a;Spring、Spring Boot、Spring MVC、MyBatis 2.大项目实践 3.源码阅读 二、JavaEE简介 B/S架构web开发流程 web前端开发&#xff08;了解&#xff09; web后端开发&#xff08;重点&#xff09; 三、…

【C++】OJ习题 篇2

&#x1f680;个人主页&#xff1a;奋斗的小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 &#x1f4a5;1、删除有序数组中的重复项&#x1f4a5;2、数组中出现次数超过一半的数字&#x1f4a5;3、最…

【Python进阶】学习Python必须要安装PyCharm。一篇文章带你总结一下安装PyCharm的注意事项,文末附带PyCharm激活码!!!

PyCharm激活码&#xff08;文末附带精品籽料&#xff09;&#xff1a; K384HW36OB-eyJsaWNlbnNlSWQiOiJLMzg0SFczNk9CIiwibGljZW5zZWVOYW1lIjoibWFvIHplZG9uZyIsImxpY2Vuc2VlVHlwZSI6IlBFUlNPTkFMIiwiYXNzaWduZWVOYW1lIjoiIiwiYXNzaWduZWVFbWFpbCI6IiIsImxpY2Vuc2VSZXN0cmljdG…

代码随想录算法训练营day58:图论08:拓扑排序精讲;dijkstra(朴素版)精讲

拓扑排序精讲 卡码网&#xff1a;117. 软件构建(opens new window) 题目描述&#xff1a; 某个大型软件项目的构建系统拥有 N 个文件&#xff0c;文件编号从 0 到 N - 1&#xff0c;在这些文件中&#xff0c;某些文件依赖于其他文件的内容&#xff0c;这意味着如果文件 A 依…

Unity 动态光照贴图,加载后显示变暗或者变白问题 ReflectionProbe的使用

动态加载光照贴图代码&#xff0c;可参考这个帖子 Unity 预制动态绑定光照贴图遇到变白问题_unity urp 动态加载光照信息 变黑-CSDN博客 这次遇到的问题是&#xff0c;在编辑器下光照贴图能正常显示&#xff0c;打出apk后光照贴图加载后变黑的问题 以下4张图代表4种状态&…

计算机毕业设计 基于SpringBoot框架的网上蛋糕销售系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

MATLAB生成mif文件

MATLAB代码 % 参数设置 N 4096; % 数据点数量 t linspace(0, 2*pi, N); % 时间向量 width 12; % 位宽% 正弦波 sine_wave 2.5 * sin(t) 2.5; % 幅度在0到5之间% 三角波 tri_wave 5 - abs(mod(t/(2*pi)*4, 2) - 1);% 方波 square_wave 2.5 * (square(t) 1); % 将范围调…

安嘉空间:智慧科技守护空间健康

在当今社会&#xff0c;随着人们对生活质量要求的不断提升&#xff0c;室内环境的健康与安全问题日益受到重视。安嘉空间&#xff0c;作为一家致力于人居健康空间技术研发的高科技企业&#xff0c;以其独创的技术和卓越的产品&#xff0c;为广大用户提供了一套全面的空间健康解…

VastBase——数据库参数调优

一、内存参数调优 数据库的复杂查询语句性能非常强的依赖于数据库系统内存的配置参数。数据库系统内存的配置参数主要包括逻辑内存管理的控制参数和执行算子是否下盘的参数&#xff1a; 1.逻辑内存管理参数&#xff1a;max_process_memory max_process_memory – shared memo…

STM32 - 笔记3

1 开发有基于寄存器和HAL库 在开发 STM32 系列微控制器时&#xff0c;你可以选择基于寄存器的开发方法或使用 STM32 HAL&#xff08;硬件抽象层&#xff09;库进行开发。两者各有优缺点&#xff0c;适用于不同的场景和开发需求。下面详细介绍两种方法的特点、使用场景以及示例…

五、实现随机地图

一、创建场景 拖拽层级面板&#xff0c;删除摄像机 二、使用Addressable 给场景设置碰撞器 三、场景切换 场景中增加一个数据集合选择场景 四、字典 1、作用 根据列表中的RoomType查找数据 创建一个RoomDataSO的列表&#xff1b;创建一个字典&#xff0c;匹配房间类型和数据…

安装MySQL,navicat以及Django配置遇到的一些问题

MySQL安装问题 安装MySQL按照了此文章&#xff1a; MySQL数据库下载及安装教程&#xff08;最最新版&#xff09;_mysql下载安装-CSDN博客https://blog.csdn.net/weixin_39289696/article/details/128850498首先是遇到了starting the server红色叉号显示 按照上面文章的介绍…

故障诊断 | 基于小波时频图与Swin Transformer的轴承故障诊断方法(PyTorch)

文章目录 文章概述程序设计参考资料文章概述 基于小波时频图与Swin Transformer的轴承故障诊断方法 针对用传统的故障诊断方法难以对非线性非平稳的柴油机故障信号进行准确高效诊断的问题, 提出基于小波时频图与Swin Transformer的故障诊断方法。该方法可以有效结合小波时频分…

Luma AI,让你的视频像电影一样精彩!附带使用教程

Luma AI&#xff0c;让你的视频像电影一样精彩&#xff01;附带使用教程 随着 AI 的应用变广&#xff0c;各类 AI 程序已逐渐普及。AI 已逐渐深入到人们的工作生活方方面面。而 AI 涉及的行业也越来越多&#xff0c;从最初的写作&#xff0c;到医疗教育&#xff0c;再到现在的…

二叉树详解(进阶)

目录 1. 二叉搜索树 1.1 基本概念 1.2 基本操作 1.3 性能分析 1.4 键值对 2. AVL树和红黑树 2.1 AVL树 2.2 红黑树 3. 红黑树模拟实现STL中的map与set 1. 二叉搜索树 1.1 基本概念 二叉搜索树&#xff08;BST&#xff0c;Binary Search Tree&#xff09;&#xff1a…

Tomcat多实例部署

文章目录 Tomcat多实例部署一、安装好 jdk1.1设置JDK环境变量 image-20240820142906811二、安装 tomcat2.1配置 tomcat 环境变量2.2修改 tomcat2 中的 server.xml 文件2.3修改各 tomcat 实例中的 startup.sh 和 shutdown.sh 文件&#xff0c;添加 tomcat 环境变量2.4启动各 tom…

【学习笔记】卫星通信发展趋势

卫星通信系统是融合现代通信技术、航天技术与计算机技术的综合应用&#xff0c;已成为国际与国内通信、国防、移动通信及广播电视领域的关键基础设施。基于其频带宽度大、通信容量高、业务兼容性强、覆盖范围广、性能稳定、地理条件适应性高及成本与距离无关等特性&#xff0c;…

uniapp scroll-view滚动触底加载 height高度自适应

背景&#xff1a; scroll-view组件是使用&#xff0c;官网说必须给一个高度height&#xff0c;否则无法滚动&#xff0c;所以刚开始设置了<scroll-view :style"height: 94vh" :scroll-y"true">设置了一个高度&#xff0c;想着vh应该挺合适的&#xf…

PhpStorm2024版设置自动换行(软换行)

Settings > Editor > General > Soft Wraps 选中并加上对应的文件

面试SQL题的水到底有多深?一文带你揭晓

不谋万世者&#xff0c;不足谋一时&#xff1b;不谋全局者&#xff0c;不足谋一域 目录 0 面试现状 1 面试SQL题目的难度及特点 1.1 题目场景化 1.2 题目算法化 1.3 方法多元化 2 破局之道 3 总结 数字化建设通关指南 主要内容&#xff1a; &#xff08;1&#xff09;SQL进阶实…