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.xdnf.cn/news/1523332.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vue3中使用supermap icilent3d for cesium

记录从头开始学习supermap icilent3d fro cesium 1.新建vue3项目 npm create vitelatest 添加这个&#xff0c;自动打开浏览器 2.使用supermap icilent3d for Cesium 复制这个Cesium&#xff0c;放到pulibc目录下面 然后分别引入css和js 然后就可以使用了&#xff0c;但是会…

Oracle 客户端 PL/SQL Developer 15.0.4 安装与使用

目录 官网下载与安装 切换中文与注册 连接Oracle数据库 tnsnames.ora 文件使用 Oracle 客户端 PL/SQL Developer 12.0.7 安装、数据导出、Oracle 执行/解释计划、for update。 官网下载与安装 1、官网&#xff1a;https://www.allroundautomations.com/products/pl-sql-d…

【STM32】通用定时器TIM(输出比较)

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 前言 输出比较简介 PWM简介 输出比较配置 初始化IO口 输出比较初始化 输出比较代码 PWM.h PWM.c main.c 应用案例 前言 建议先阅读这篇博客&#xff0c;理解时基单元的配置 【…

CDGA|数据治理:构建高效数据管理体系的实践路径

在当今数字化时代&#xff0c;数据已成为企业最宝贵的资产之一&#xff0c;其质量、安全性和有效利用率直接影响着企业的决策能力、运营效率和市场竞争力。因此&#xff0c;数据治理作为确保数据质量、促进数据价值最大化的关键环节&#xff0c;其重要性日益凸显。本文将从几个…

机械学习—零基础学习日志(概率论总笔记1)

概率论的起源 在历史上有明确记载的最早研究随机性的数学家是帕斯卡和费马。帕斯卡就是最早发明机械计算机的那位数学家&#xff0c;他并不是赌徒&#xff0c;但是他有些赌徒朋友&#xff0c;那些人常常玩一种掷骰子游戏&#xff0c;游戏规则是由玩家连续掷4次骰子&#xff0c…

【王树森】Vision Transformer (ViT) 用于图片分类(个人向笔记)

图片分类任务 给定一张图片&#xff0c;现在要求神经网络能够输出它对这个图片的分类结果。下图表示神经网络有40%的信心认定这个图片是狗 ResNet&#xff08;CNN&#xff09;曾经是是图像分类的最好模型在有足够大数据做预训练的情况下&#xff0c;ViT要强于ResNetViT 就是Tr…

(十五)SpringCloudAlibaba-Sentinel持久化到Nacos

前言 在前面我们已经将Sentinel配置的规则持久化到系统的文件中。本章节我们将Sentinel持久化到Nacos中; 传送门(Sentinel数据持久化到文件)https://blog.csdn.net/weixin_45876411/article/details/140742963 默认情况下 Sentinel 只能接收到 Nacos 推送的消息&#xff0c;但…

火情监测识别摄像机

火情监测识别摄像机 是一种用于监测和识别火灾风险的设备&#xff0c;通常用于森林、草原以及其他火灾易发区域。这种摄像机能够实时监测周围的环境&#xff0c;并使用图像识别技术来识别火灾的迹象。 这些摄像机通常配备红外热成像技术和视频分析算法&#xff0c;可以在白天和…

程序二义性举例

// 程序二义性.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //#include <iostream> using namespace std; void f(int x) {cout << "---" << x << endl;} void f(int x,int y10) {cout << "" &l…

客流预测 | 基于Transformer下车站点客流推断研究(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于Transformer的车站客流推断研究是指利用Transformer模型来预测车站的客流情况。Transformer是一种强大的深度学习模型&#xff0c;特别擅长处理序列数据。研究可以为城市交通管理提供重要决策支持&#xff0c;帮…

【系统分析师】-面向对象方法

目录 1、基本概念 2、UML 2.1、基本结构 2.1.1.构造块 2.1.1.1、事物 2.1.1.2、关系 2.1.1.3、图形 2.1.2.规则 2.1.3.公共机制 2.2、41视图 3、面向对象分析OOA 3.1、用例模型 3.2、分析模型 4、面向对象设计OOD 4.1、细分 4.2、设计原则 5、面向对象的程序设…

传统CV算法——基于harris检测算法实现角点检测

角点 角点是图像中的一个特征点&#xff0c;指的是两条边缘交叉的点&#xff0c;这样的点在图像中通常表示一个显著的几角。在计算机视觉和图像处理中&#xff0c;角点是重要的特征&#xff0c;因为它们通常是图像中信息丰富的区域&#xff0c;可以用于图像分析、对象识别、3D…

大模型(LLM)和知识库的基础介绍

文章目录 概要整体架构流程结合LLM与RAP的优势小结 概要 随着自然语言处理技术的发展&#xff0c;大型语言模型&#xff08;LLM&#xff09;已经成为了人工智能领域中的一个重要组成部分。这些模型通常具有数亿到数千亿个参数&#xff0c;能够理解和生成自然语言&#xff0c;从…

LabVIEW程序员错误排查思路

当LabVIEW程序员在开发过程中遇到难以解决的错误且网上搜不到答案时&#xff0c;需要采取系统性的方法进行排查和解决。这包括回顾代码逻辑、深入理解LabVIEW的底层机制、参考专业文献和求助社区等方式。下面将从多角度详细解读专业程序员在面对这种困境时的应对策略&#xff0…

网络安全等级保护:等级保护工作、分级保护工作、密码管理工作三者之间的关系

上次我整理了一篇文字叫《等级保护、等级保护测评、分级保护测评、密码保护测评之间的区别与联系》&#xff0c;后来发现这种措辞还是存在问题&#xff0c;今天在此重新做个探讨&#xff0c;同时进行更正。我们很多从事信息安全行业的人&#xff0c;交流时常常会提及“等保”“…

【淘宝采集项目经验分享】商品评论采集 |商品详情采集 |关键词搜索商品信息采集

商品评论采集 1、输入商品ID 2、筛选要抓取评论类型 3、填写要抓取的页数 4、立刻提交-启动测试 5、等爬虫结束后就可以到“爬取结果”里面下载数据 商品详情采集 1、输入商品ID 2、立刻提交-启动爬虫 3、等爬虫结束后就可以到“爬取结果”里面下载数据 taobao.item_…

数据结构排序方法总结

给定两个数组A,B&#xff0c;将A,B排序合并成一个数组&#xff0c;输出升序排列后的新数组。数组A,B中为整数&#xff0c;字母。 下面是代码&#xff1a; import java.util.Arrays;public class Solution15 {//冒泡排序public static void bubbleSort(String[] array) {int n…

俄罗斯Ozon选品三要素,简单实用的选品方法

在 Ozon 上选品可以参考以下三个要素&#xff1a; 要素一&#xff1a;市场需求 关注热门品类&#xff1a;从 Ozon 的销售数据和市场趋势来看&#xff0c;像电子产品&#xff08;如手机、耳机、智能穿戴设备等&#xff09;、时尚服饰&#xff08;包括流行服装、鞋类、配饰&…

电商数据驱动决策:京东商品详情API返回值的力量

在电商数据驱动决策的过程中&#xff0c;京东商品详情API返回值的力量不容忽视。这些返回值包含了丰富的商品信息&#xff0c;如商品标题、价格、图片、规格参数、用户评价等&#xff0c;为电商企业提供了强大的数据支持&#xff0c;帮助企业更加精准地把握市场动态&#xff0c…

开源项目|聚合支付工具,封装了某宝、某东、某银、PayPal等常用的支付方式

前言 IJPay是一款开源的支付SDK&#xff0c;它集成了微支付、某宝支付、银联支付等多种支付方式&#xff0c;为开发者提供了一种简单、高效的方式来处理支付问题。以下是IJPay的一些主要特点&#xff1a; 支持多种支付方式&#xff1a;IJPay支持微信支付、支付宝支付、银联支付…