使用redis进行短信登录验证(验证码打印在控制台)

使用redis进行短信登录验证

  • 一、流程
    • 1. 总体流程图
    • 2. 流程文字讲解:
    • 3.代码
      • 3.1 UserServiceImpl:(难点)
      • 3.2 拦截器LoginInterceptor:
      • 3.3 拦截器配置类:
    • 4 功能实现,成功存入redis

(黑马点评)

一、流程

1. 总体流程图

在这里插入图片描述

2. 流程文字讲解:

通过前端提供的手机号,进行判断符不符合11位的格式,如果不符合,返回错误信息,并且提示手机格式错误;判断符合格式,则生成一个6位的随机验证码。然后存到redis中。这个时候,只是简单的key-value,所以我们使用string类型。
我们现在生成了验证码,在进行校验之前,还需要验证是否和之前的手机号一致。然后校验验证码和我们redis中的验证码是否一致。不一致,返回错误信息;一致,查询手机号是否已经存在,如果User为空,则创建用户。然后保存信息到redis,作为登录令牌通过拦截器拦截。
生成一个token作为登录令牌,因为我们现在是需要通过token来携带我们的用户信息,所以我们使用Hash类型。第一步,把我们的User对象转为UserDTO类型。我们要存Hash的数据类型,就需要使用HashMap对象,所以,还需要把UserDTO转为HashMap(比较麻烦,详细见代码),然后存入并设置有效期。
对于拦截器:我们要使用StringRedisTemplate对象,但拦截器是我们自己写的,不进入Spring容器里面,所以需要通过构造函数来拿到StringRedisTemplate对象。
然后从请求头中拿到token,如果没有token,拦截,返回错误。然后从token中拿到UserMap对象。然后判断是否存在,不存在拦截。然后将Hash对象转为UserDTO对象,存入ThreadLocal,然后刷新有效期。

3.代码

3.1 UserServiceImpl:(难点)

package com.hmdp.service.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RegexUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;import static com.hmdp.utils.RedisConstants.LOGIN_CODE_KEY;
import static com.hmdp.utils.RedisConstants.LOGIN_CODE_TTL;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Resourceprivate StringRedisTemplate stringRedisTemplate;//在之前csdn我们配置过。@Overridepublic Result sendCode(String phone, HttpSession session) {//1.校验手机号if (RegexUtils.isPhoneInvalid(phone)){//RegexUtils去校验手机号是否无效,再utils中//2.如果不符合,返回错误信息return Result.fail("手机号格式错误");}//3.符合,生成验证码String code = RandomUtil.randomNumbers(6);//生成一个6位的随机数字验证码/*    //4.保存验证码到session,session.setAttribute("code",code);*///4.保存验证码到redis中  = set key value ex 120stringRedisTemplate.opsForValue().set("login:code:"+phone,code,2, TimeUnit.MINUTES);//这里就把验证码保存到了redis中,为了防止数据存储过多,设置存储时间stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);//这里是把我们用到的常量定义到常量类里面。//5.发送验证码,如果我们要发送短信的话,需要通过第三方平台,比如阿里云之类的,所以我们直接使用log日志输出log.debug("发送短信验证码成功,验证码:"+ code);//返回okreturn Result.ok();}//使用redis的登录和注册@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//1.校验手机号//因为我们需要确定,登录的时候,手机号还是不是一个String phone =loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)){//RegexUtils去校验手机号是否无效,再utils中//2.如果不符合,返回错误信息return Result.fail("手机号格式错误");}//2.从redis过去验证码然后校验验证码String CacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY+phone);//从redis中获取验证码String code = loginForm.getCode();if (CacheCode==null||!CacheCode.equals(code)){//3.不一致,报错return Result.fail("验证码错误");}//4.一致,根据手机号查询用户 select * from tb_user where phone = ?  =query,因为,我们在这个类上面extends ServiceImpl<UserMapper, User>,所以可以使用mybatisPlusUser user = query().eq("phone", phone).one();//one()是查询一个,如果查询不到,返回null//5.判断用户是否存在if (user == null) {//6.不存在,创建新用户,保存user =  creteUserWithPhone(phone);}//7.保存用户信息到Redis//7.1 生成一个token,作为登录令牌String token = UUID.randomUUID().toString();//toString是转为String类型//7.2 将User对象转为hash存储UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);//7.3 存储 难点/*** 因为  Map<String, Object>是我们所有属性都是string的时候,才可以完全存进去,但我们的id是Long类型的* 所以我们使用工具类对其中的失败的值进行转化为string*   Map<String, Object>是可以自定义方法的,我们现在需要的是一个HashMap,所以userDTO,new HashMap<>(),*   然后使用  CopyOptions.create().setIgnoreNullValue(true).忽略空的值*    setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));失败的name和value我们只把失败的*    value转化为string类型不改id*/Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));//把userDTO转为Map<>String tokenKey="login:token:"+token;stringRedisTemplate.opsForHash().putAll(tokenKey,userMap);//因为我们要往hash中存储对象,多个值,但putall存储的是Map<>//                              我们不要直接存这个名字,和前面phone一样,防止我们混淆//7.4 设置token的有效期stringRedisTemplate.expire(tokenKey,30,TimeUnit.MINUTES);//session是30min没有访问,才删除,如果要做到一样,可以使用拦截器return Result.ok(token);}//每一个session都有一个sessionid,当我们访问tomcat的时候,就已经自动带着了,所以,又sessionId就能知道是哪个用户private User creteUserWithPhone(String phone) {//1.创建用户User user =new User();user.setPhone(phone);user.setNickName("User_"+RandomUtil.randomString(10));//2.保存用户save(user);return user;}
}/*传统session里面@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//1.校验手机号//因为我们需要确定,登录的时候,手机号还是不是一个String phone =loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)){//RegexUtils去校验手机号是否无效,再utils中//2.如果不符合,返回错误信息return Result.fail("手机号格式错误");}//2.校验验证码Object CacheCode = session.getAttribute("code");String code = loginForm.getCode();if (CacheCode==null||!CacheCode.toString().equals(code)){//3.不一致,报错return Result.fail("验证码错误");}//4.一致,根据手机号查询用户 select * from tb_user where phone = ?  =query,因为,我们在这个类上面extends ServiceImpl<UserMapper, User>,所以可以使用mybatisPlusUser user = query().eq("phone", phone).one();//one()是查询一个,如果查询不到,返回null//5.判断用户是否存在if (user == null) {//6.不存在,创建新用户,保存user =  creteUserWithPhone(phone);}//7.保存用户信息到sessionsession.setAttribute("User", BeanUtil.copyProperties(user, UserDTO.class));//将user对象转换成UserDTO对象,再保存到session中return Result.ok();}//每一个session都有一个sessionid,当我们访问tomcat的时候,就已经自动带着了,所以,又sessionId就能知道是哪个用户private User creteUserWithPhone(String phone) {//1.创建用户User user =new User();user.setPhone(phone);user.setNickName("User_"+RandomUtil.randomString(10));//2.保存用户save(user);return user;}
}
*/

3.2 拦截器LoginInterceptor:

package com.hmdp.utils;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;
import java.util.concurrent.TimeUnit;public class LoginInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;//这里是由下面这个构造函数,在MVC拦截器中传递进来的对象得到的public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}//这个类的对象不是由spring创建的,所以我们不能使用@Rouse,只能使用构造函数//redis的@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.获取请求头中的token,String token = request.getHeader("authorization");if (StrUtil.isBlank(token)) {//使用工具类StrUtil中的方法isBlank//2.如果请求头中没有token,拦截  返回401状态码  未授权的意思response.setStatus(401);return false;}//2.基于token获取redis的用户Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);//这里使用的是redis的hash结构
/*session.setAttribute("sessionName",Object); //保存//用来设置session值的,sessionName是名称,object是你要保存的对象。session.getAttribute("sessionName");  //取得//用来得到对应名称的session值,即得到object对象,注意需要进行类型转换!
*///3.判断用户是否存在if (userMap.isEmpty()) {//如果为空//4.不存在,拦截  返回401状态码  未授权的意思response.setStatus(401);return false;}//将查询到的Hash转为UserDTO对象UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//将userMap转存为UserDTp,不忽略异常
//5.存在,保存到ThreadLocal  对ThreadLocal进行了规范和重写。UserHolder.saveUser(userDTO);//把user保存到ThreadLocal,对user进行强转//刷新token有效期stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);//6.放行return true;}/*    session的
@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.获取sessionHttpSession session = request.getSession();//2.获取session的用户Object user = session.getAttribute("user");*//*session.setAttribute("sessionName",Object); //保存//用来设置session值的,sessionName是名称,object是你要保存的对象。session.getAttribute("sessionName");  //取得//用来得到对应名称的session值,即得到object对象,注意需要进行类型转换!
*//*//3.判断用户是否存在if (user == null) {//4.不存在,拦截  返回401状态码  未授权的意思response.setStatus(401);return false;}//5.存在,保存到ThreadLocal  对ThreadLocal进行了规范和重写。UserHolder.saveUser((UserDTO)user);//把user保存到ThreadLocal,对user进行强转//6.放行return true;}*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();//移除用户}
}

3.3 拦截器配置类:

package com.hmdp.config;import com.hmdp.utils.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;/*** 这个类上面有@Configuration,是由spring来创建的,所以我们可以使用  @Resource来引入对象,再放到下面这个* LoginInterceptor构造函数里面,传递给LoginInterceptor类* @param registry*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns(//放行的接口"/blog/hot","/shop/**","/upload/**","/voucher/**","/shop-type/**","/user/code","/user/login");}
}

4 功能实现,成功存入redis

在这里插入图片描述

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

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

相关文章

java中 使用数组实现需求小案例

Date: 2024.04.08 18:32:57 author: lijianzhan 需求实现&#xff1a; 设计一个java类&#xff0c;java方法&#xff0c;根据用户手动输入的绩点&#xff0c;从而获取到绩点最高的成绩。 实现业务逻辑的代码块 import java.util.Scanner;public class PointDemo {/*** 需求&…

Spring相关面试题(四)

49 JavaConfig方式如何启用AOP?如何强制使用cglib&#xff1f; 在JavaConfig类&#xff0c;加上EnableAspectJAutoProxy 如果要强制使用CGLIB动态代理 &#xff0c;加上(proxyTargetClass true) 加上(exposeProxy true) 就是将对象暴露到线程池中。 50 介绍AOP在Spring中…

详解TCP和UDP通信协议

目录 OSI的七层模型的主要功能 tcp是什么 TCP三次握手 为什么需要三次握手&#xff0c;两次握手不行吗 TCP四次挥手 挥手会什么需要四次 什么是TCP粘包问题&#xff1f;发生的原因 原因 解决方案 UDP是什么 TCP和UDP的区别 网络层常见协议 利用socket进行tcp传输代…

KIVY Button¶

Button — Kivy 2.3.0 documentation Button Jump to API ⇓ Module: kivy.uix.button Added in 1.0.0 The Button is a Label with associated actions that are triggered when the button is pressed (or released after a click/touch). To configure the button, the s…

【论文速读】| 用于安全漏洞防范的人工智能技术

本次分享论文&#xff1a;Artificial Intelligence Techniques for Security Vulnerability Prevention 基本信息 原文作者&#xff1a;Steve Kommrusch 作者单位&#xff1a;Colorado State University, Department of Computer Science, Fort Collins, CO, 80525 USA 关键…

ISO/OSI七层模型

ISO:国际标准化/ OSI:开放系统互联 七层协议必背图 1.注意事项&#xff1a; 1.上三层是为用户服务的&#xff0c;下四层负责实际数据传输。 2.下四层的传输单位&#xff1a; 传输层&#xff1b; 数据段&#xff08;报文&#xff09; 网络层&#xff1a; 数据包&#xff08;报…

Vue项目openlayers中使用jsts处理wkt和geojson的交集-(geojson来源zpi解析)

Vue项目openlayers中使用jsts处理wkt和geojson的交集-(geojson来源zpi解析) 读取压缩包中的shape看上一篇笔记&#xff1a;Vue项目读取zip中的ShapeFile文件&#xff0c;并解析为GeoJson openlayers使用jsts官方示例&#xff1a;https://openlayers.org/en/latest/examples/j…

科技创新引领水利行业升级:深入分析智慧水利解决方案的核心价值,展望其在未来水资源管理中的重要地位与作用

目录 引言 一、智慧水利的概念与内涵 二、智慧水利解决方案的核心价值 1. 精准监测与预警 2. 优化资源配置 3. 智能运维管理 4. 公众参与与决策支持 三、智慧水利在未来水资源管理中的重要地位与作用 1. 推动水利行业转型升级 2. 保障国家水安全 3. 促进生态文明建设…

vb.netcad二开自学笔记5:ActiveX链接CAD的.net写法

一、必不可少的对象引用 使用activex需要在项目属性中勾选以下两个引用&#xff0c;若找不到&#xff0c;则浏览定位直接添加下面两个文件&#xff0c;可以看到位于cad的安装路径下&#xff0c;图中的3个mgd.dll也可以勾选。 C:\Program Files\Autodesk\AutoCAD 2024\Autodes…

实战 | YOLOv8使用TensorRT加速推理教程(步骤 + 代码)

导 读 本文主要介绍如何使用TensorRT加速YOLOv8模型推理的详细步骤与演示。 YOLOv8推理加速的方法有哪些? YOLOv8模型推理加速可以通过多种技术和方法实现,下面是一些主要的策略: 1. 模型结构优化 网络剪枝:移除模型中不重要的神经元或连接,减少模型复杂度。 模型精…

中文大模型基准测评2024上半年报告

中文大模型基准测评2024上半年报告 原创 SuperCLUE CLUE中文语言理解测评基准 2024年07月09日 18:09 浙江 SuperCLUE团队 2024/07 背景 自2023年以来&#xff0c;AI大模型在全球范围内掀起了有史以来规模最大的人工智能浪潮。进入2024年&#xff0c;全球大模型竞争态势日益加…

Obsidian 文档编辑器

Obsidian是一款功能强大的笔记软件 Download - Obsidian

降Compose十八掌之『见龙在田』| Modifier

公众号「稀有猿诉」 原文链接 降Compose十八掌之『见龙在田』| Modifier 通过前面的文章我们学会了如何使用元素来构建和填充我们的UI页面&#xff0c;但这只完成了一半&#xff0c;元素还需要装饰&#xff0c;以及进行动画和事件响应&#xff0c;这才能生成完整的UI。这…

2.5章节python中布尔类型

在Python中&#xff0c;布尔类型&#xff08;Boolean type&#xff09;用于表示真&#xff08;True&#xff09;或假&#xff08;False&#xff09;的值。这是编程中非常基础且重要的一个概念&#xff0c;因为它允许程序进行条件判断&#xff0c;从而根据条件执行不同的代码块。…

智慧校园行政办公-红头文件功能概述

在智慧校园的行政办公系统中&#xff0c;红头文件的管理功能是一项重要的组成部分&#xff0c;它极大地提升了文件处理的效率与规范性。该功能围绕文件的创建、审批、归档等关键环节&#xff0c;进行了全面的数字化改造。 首先&#xff0c;系统内置了多种标准化的红头文件模板&…

一文实践强化学习训练游戏ai--doom枪战游戏实践

一文实践强化学习训练游戏ai–doom枪战游戏实践 上次文章写道下载doom的环境并尝试了简单的操作&#xff0c;这次让我们来进行对象化和训练、验证&#xff0c;如果你有基础&#xff0c;可以直接阅读本文&#xff0c;不然请你先阅读Doom基础知识&#xff0c;其中包含了下载、动作…

打开ps提示dll文件丢失如何解决?教你几种靠谱的方法

在日常使用电脑过程中&#xff0c;由于不当操作&#xff0c;dll文件丢失是一种常见现象。当dll文件丢失时&#xff0c;程序将无法正常运行&#xff0c;比如ps&#xff0c;pr等待软件。此时&#xff0c;我们需要对其进行修复以恢复其功能&#xff0c;下面我们一起来了解一下出现…

【堆 (优先队列) 扫描线】218. 天际线问题

本文涉及知识点 堆 &#xff08;优先队列) 扫描线 LeetCode218. 天际线问题 城市的 天际线 是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度&#xff0c;请返回 由这些建筑物形成的 天际线 。 每个建筑物的几何信息由数组 buildings 表示&…

景芯SoC训练营DFT debug

景芯训练营VIP学员在实践课上遇到个DFT C1 violation&#xff0c;导致check_design_rule无法通过&#xff0c;具体报错如下&#xff1a; 遇到这个问题第一反映一定是确认时钟&#xff0c;于是小编让学员去排查add_clock是否指定了时钟&#xff0c;指定的时钟位置是否正确。 景芯…

2024年信息系统项目管理师1批次上午客观题参考答案及解析(3)

51、探索各种选项&#xff0c;权衡包括时间与成本、质量与成本、风险与进度、进度与质量等多种因素&#xff0c;在整个过程中&#xff0c;舍弃无效或次优的替代方案&#xff0c;这种不确定性应对方法是()。 A&#xff0e;集合设计 B&#xff0e;坚韧性 C&#xff0e;多种结果…