Redis的使用(三)常见使用场景-session共享

1.绪论

redis是一款高性能的缓存框架,那它在实际开发过程中有哪些作用呢?今天我们就来聊一聊。

2.session共享

2.1  问题描述

现在所有的网页都有一个功能,就是在用户第一次登陆成功过后,再次进入到这个页面,就不用二次登陆了,那这种场景式如何实现的呢?

2.2 传统登录流程

传统方式是在用户登录过后,用户会校验登录是否成功,如果登录成功,会将用户信息保存到session中,后面用户再次访问的时候,会通过cookie的方式将用户的sessionid携带过来,如果用户在session中根据sessionId查询到用户信息,便表示当前用户已经登录过。步骤如下:

2.2.1 发送短信验证码

发送短信验证码:

1.用户提交手机号,点击登录;

2.查询数据库,校验用户是否存在;

3.如果用户不存在,生成验证码,并将验证码保存到session中;

4.给用户返回验证码

 public Result sendCode(String phone, HttpSession session) {//1.校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}//2.生成验证码String code = RandomUtil.randomNumbers(6);//3.将验证码放入到session中session.setAttribute("code", code);//4.给用户发送验证码log.debug("用户验证码为;{}", code);//5.返回成功return Result.ok();}

2.2.2 登录注册 

用户登录注册流程:

1.用户填写手机号和验证码;

2.在session中根据前端携带的cookie中的sessionId查询到当前用户的验证码,并于用户的验证码进行比对;

3.验证通过,根据手机号查询数据库,如果数据库存在该用户,便将用户保存到session中;

4.如果用户不存在,便创建用户并插入到数据库,同时保存到session中;

public Result login(LoginFormDTO loginForm, HttpSession session) {String phone = loginForm.getPhone();//1.校验手机号if (RegexUtils.isPhoneInvalid(loginForm.getPhone())) {// 2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}// 2.获取缓存中的验证码String code = loginForm.getCode();Object codeCache = session.getAttribute("code");//3.比较验证码是否正确if (codeCache == null || !code.equals(codeCache)) {return Result.fail("登录失败");}//4.如果比对通过,查询数据库User user = query().eq("phone", phone).one();if (user == null) {//5.未查询到用户,表示第一次注册,直接新建一个用户并插入User user = new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));// 2.保存用户save(user);return user;}//5.保存到session中session.setAttribute("user", user);return Result.ok();}

2.2.3 再次登录 

用户再次登录流程:

1.用户调用其他接口,会携带cookie;

2.根据cookie中的sessionId在session中查询;

3.如果session中存在用户,便将用户封装到ThreadLocal中,调用接口;

4.如果不存在用户,便返回登录失败;

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.获取sessionHttpSession session = request.getSession();//2.获取session中的用户Object user = session.getAttribute("user");//3.判断用户是否存在if(user == null){//4.不存在,拦截,返回401状态码response.setStatus(401);return false;}//5.存在,保存用户信息到ThreadlocalUserHolder.saveUser((User)user);//6.放行return true;}
}

2.3 传统登录流程的缺点

1.用于用户信息是存储在session中的,而session是属于一个Tomcat实例,如果采用分布式部署的话,当请求到tomcat1的话,保存到tomcat1的话,下次登录请求到tomcat2的话,便查询不到用户信息。所以如何将用户信息同步到整个集群,便成了一个问题。

2.4 基于redis实现的登录

redis的登录其实就是将用户信息放到redis中,下次登录的时候,直接从redis的中获取。

2.4.1 发送短信验证码

发送短信验证码:

1.用户提交手机号,点击登录;

2.查询数据库,校验用户是否存在;

3.如果用户不存在,生成验证码,并将验证码保存到redis中;

4.给用户返回验证码

 public Result sendCode(String phone) {//1.校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}//2.生成验证码String code = RandomUtil.randomNumbers(6);//3.将验证码放入到redis中stringRedisTemplate.opsForValue().set("code:" + phone, code);//4.给用户发送验证码log.debug("用户验证码为;{}", code);//5.返回成功return Result.ok();}

2.4.2 登录注册

用户登录注册流程:

1.用户填写手机号和验证码;

2.在redis中根据用户的手机号作为key查询,查询到用户的验证码,并校验验证码

3.验证通过,根据手机号查询数据库,如果数据库存在该用户,便生成一个token用来标识该用户,并且将其作为key,user信息作为value存储到redis中。

4.如果用户不存在,便创建用户并插入到数据库,同时保存到redis中;

   public Result login(LoginFormDTO loginForm, HttpSession session) {// 1.校验手机号String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}// 3.从redis获取验证码并校验String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);String code = loginForm.getCode();if (cacheCode == null || !cacheCode.equals(code)) {// 不一致,报错return Result.fail("验证码错误");}// 4.一致,根据手机号查询用户 select * from tb_user where phone = ?User user = query().eq("phone", phone).one();// 5.判断用户是否存在if (user == null) {// 6.不存在,创建新用户并保存user = createUserWithPhone(phone);}// 7.保存用户信息到 redis中// 7.1.随机生成token,作为登录令牌String token = UUID.randomUUID().toString(true);// 7.2.将User对象转为HashMap存储UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));// 7.3.存储String tokenKey = LOGIN_USER_KEY + token;stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);// 7.4.设置token有效期stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);// 8.返回tokenreturn Result.ok(token);}

注:为什么这里要生成一个sessionId作为唯一标识。原因前端请求用户信息的时候,会将token放到请求头中,如果是将phone放到请求头中,会有安全问题。

2.4.3 再次登录

1.获取到用户的token信息

2.根据token从redis中取出userInfo信息

3.如果userInfo信息不存在,便登录失败

4.如果userInfo信息存在,便重新设置toke在redis中的存活时间,并且将userInfo存储到ThreadLocal中。

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.获取请求头中的tokenString token = request.getHeader("authorization");if (StrUtil.isBlank(token)) {return true;}// 2.基于TOKEN获取redis中的用户String key  = LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);// 3.判断用户是否存在if (userMap.isEmpty()) {return true;}// 5.将查询到的hash数据转为UserDTOUserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);// 6.存在,保存用户信息到 ThreadLocalUserHolder.saveUser(userDTO);// 7.刷新token有效期stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);// 8.放行return true;}

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

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

相关文章

大模型预训练-数据准备

名词释义 预训练&#xff1a;大模型训练的第一个阶段数据集污染&#xff1a;预训练数据中包含测试数据中的部分或全部 数据来源 通用数据 网页书籍 专用数据 多语文本科学论文代码 数据预处理 质量过滤 基于启发式规则 规则建议 基于语种过滤&#xff08;过滤不支持的语…

kafka 消费者

消费者 消费者。消费者连接到Kafka上并接收消息&#xff0c;进而进行相应的业务逻辑处理。 消费组 消费者负责订阅Kafka中的主题&#xff0c;并且从订阅的主题上拉取消息。 消费组&#xff1a;每个消费者都有一个对应的消费组&#xff0c;每一个分区只能被一个消费组中的一个…

如何控制代码质量

大家好&#xff0c;我是嘻嘻&#xff0c;你们厂里是如何控制代码的质量的说道说道 最近的工作&#xff0c;主要是要偏重项目的质量管理这块&#xff0c;项目管理的现在一切以质量为导向&#xff0c;成本和质量冲突优先质量&#xff0c;进度与质量冲突优先质量&#xff0c;真正的…

方法引用 异常 file

目录 一.方法引用 1.方法引用概述 2.引用静态方法 3.引用成员方法 i.引用其他成员方法 ii.引用本类成员方法 iii.引用父类成员方法 4.引用构造方法 5.其他调用方式 i.使用类名引用成员方法 ii.引用数组的构造方法 二、异常 1.异常的作用 2.异常的处理方式 i.JVM…

6.Python学习:异常和日志

1.异常的抓取 1.1异常的概念 使用异常前&#xff1a; print(1/0)使用异常后&#xff1a;错误提示更加友好&#xff0c;不影响程序继续往下运行 try:print(10/0) except ZeroDivisionError:print("0不能作为分母")1.2异常的抓取 第一种&#xff1a;如果提前知道可…

2020 ICPC Shanghai Site B. Mine Sweeper II 题解 构造 鸽巢原理

Mine Sweeper II 题目描述 A mine-sweeper map X X X can be expressed as an n m n\times m nm grid. Each cell of the grid is either a mine cell or a non-mine cell. A mine cell has no number on it. Each non-mine cell has a number representing the number of…

Spring Boot使用@RestController注解的控制器(Controller)返回Map时,遇到序列化问题

在Spring Boot中使用RestController注解的控制器&#xff08;Controller&#xff09;返回Map时&#xff0c;如果遇到序列化问题&#xff0c;这通常是因为Map中的某些值类型无法被Spring Boot默认的JSON序列化库&#xff08;通常是Jackson&#xff09;正确处理。以下是一些可能导…

遍历请求后端数据引出的数组forEach异步操作的坑

有一个列表数据&#xff0c;每项数据里有一个额外的字段需要去调另外一个接口才能拿到&#xff0c;后端有现有的这2个接口&#xff0c;现在临时需要前端显示出来&#xff0c;所以这里需要前端先去调列表数据的接口拿到列表数据&#xff0c;然后再遍历请求另外一个接口去拿到对应…

【python技巧】pytorch网络可视化

参考 https://blog.csdn.net/qq_40726937/article/details/106122082 1. graphviz torchviz 环境安装简单 pip install torchviz pip install graphviz代码 import torch from torchvision import model from torchviz import make_dotmodels models.resnet18() x torc…

生产环境中秒杀接口并发量剧增与负载优化策略探讨

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 1. 实施限流措施 1.1 令牌桶算法&#xff1a; 1.2 漏…

红酒知识百科:从入门到精通

红酒&#xff0c;这个深邃而迷人的世界&#xff0c;充满了无尽的知识与奥秘。从葡萄的选择、酿造工艺&#xff0c;到品鉴技巧&#xff0c;每一步都蕴藏着深厚的文化底蕴和精细的技艺。今天&#xff0c;就让我们一起踏上这场红酒知识之旅&#xff0c;从入门开始&#xff0c;逐步…

gpt-4o看图说话-根据图片回答问题

问题&#xff1a;中国的人口老龄化究竟有多严重&#xff1f; 代码下实现如下&#xff1a;&#xff08;直接调用openai的chat接口&#xff09; import os import base64 import requests def encode_image(image_path): """ 对图片文件进行 Base64 编码 输入…

【刷题汇总 -- 求最小公倍数、数组中的最长连续子序列、字母收集】

C日常刷题积累 今日刷题汇总 - day0081、求最小公倍数1.1、题目1.2、思路1.3、程序实现 -- 穷举法1.2、程序实现 -- 辗转相除法 2、数组中的最长连续子序列2.1、题目2.2、思路2.3、程序实现 3、字母收集3.1、题目3.2、思路3.3、程序实现 4、题目链接 今日刷题汇总 - day008 1、…

Windows C++ vs2022环境中下载、安装和使用osmesa

第一步&#xff1a;安装 MinGW-w64 请参考这篇文章进行安装&#xff1a; 在Windows中安装MinGW-w64最新版本 第二步&#xff1a;安装DirectX SDK 请参考这篇文章进行安装&#xff1a; 下载安装Microsoft DirectX SDK(June 2010) 第三步&#xff1a;安装Windows SDK 请参考这篇…

重定向(Redirect)和转发(Forward)

目录 重定向(Redirect) 转发(Forward) 在HTTP通信和Web开发中,重定向(Redirect)和转发(Forward)是两种常见的导航机制,它们各自具有不同的特点和适用场景。 forward是转发,foward url不会发生改变,forward可以共享request里的数据,forward 比 redirect 效率高。…

LeetCode 算法:课程表 c++

原题链接&#x1f517;&#xff1a;课程表 难度&#xff1a;中等⭐️⭐️ 题目 你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出&#xff0c;其中 prerequisites[i]…

oracle索引字段存储数据过长,导致索引失效

1&#xff1a;短位数据&#xff0c;索引生效 2&#xff1a;长位索引&#xff0c;索引不生效 此问题发现于6月中旬&#xff0c;线上问题优化。引以为戒。 解决&#xff1a; 并未解决索引不生效问题&#xff0c; 但是基于优化查询&#xff0c;是的查询保持毫秒级

第一个基于FISCOBCOS的前后端项目(发行转账)

本文旨在介绍一个简单的基于fiscobcos的前后端网站应用。Springbootjs前后端不分离。 所使用到的合约也是一个最基本的。首先您需要知道的是完整项目分为三部分&#xff0c;1是区块链平台webase搭建&#xff08;此项目使用节点前置webase-front即可&#xff09;&#xff0c;2是…

[stm32f407]GPIO配置方式

GPIO模式&#xff1a; GPIO_InitStructure.GPIO_ModeGPIO_Mode_IN;GPIO_InitStructure.GPIO_OTypeGPIO_OType_PP;//是否需要GPIO_InitStructure.GPIO_PinGPIO_Pin_11;GPIO_InitStructure.GPIO_PuPdGPIO_PuPd_UP;//上拉GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_Mo…

什么是防抖和节流?如何理解它们并在不同的场景条件下灵活运用?

&#x1f64b;‍♂️ 什么是防抖和节流&#xff1f; 防抖&#xff08;Debouncing&#xff09;和节流&#xff08;Throttling&#xff09;是两种常见的性能优化技术&#xff0c;常用于控制某些操作&#xff08;例如事件处理器&#xff09;的频率&#xff0c;从而减少资源消耗并…