使用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.rhkb.cn/news/373449.html

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

相关文章

悠律凝声环Ringbuds Pro耳机:素皮纹理质感独一档,音质也拉满

悠律&#xff08;UMELODY&#xff09;推出的这款新品——凝声环开放式耳机&#xff0c;以其独特的设计风格和出色的音质表现赢得了众多消费者的喜爱。 在外观上&#xff0c;凝声环采用了时尚潮酷的设计理念&#xff0c;并且采用简约典雅素皮工艺&#xff0c;首次将“素皮”材料…

QT文件生成可执行的exe程序

将qt项目生成可执行的exe程序可按照以下步骤进行&#xff1a; 1、在qt中构建运行生成.exe文件&#xff1b; 2、从自定义的路径中取出exe文件放在一个单独的空文件夹中&#xff08;exe文件在该文件夹中的release文件夹中&#xff09;&#xff1b; 3、从开始程序中搜索qt&#xf…

提升Selenium在Chrome上的HTML5视频捕获效果的五个方法

在使用Selenium进行网页自动化测试时&#xff0c;捕获HTML5视频是一个常见的需求。然而&#xff0c;许多开发者发现&#xff0c;在使用Chrome浏览器时&#xff0c;视频捕获效果并不理想&#xff0c;经常出现视频背景为空白的问题。本文将概述五种方法&#xff0c;帮助提升Selen…

品牌渠道低价管控的思考与策略

消费者在购买产品时追求低价、货比三家&#xff0c;这无可非议。然而&#xff0c;产品低价就一定是好事吗&#xff1f;倘若品牌为了迎合消费者对低价的需求&#xff0c;一味追求低价引流&#xff0c;最终的结果只能是从源头压价&#xff0c;比如在原材料的选购、供应商的选择上…

校园运动用品租借租赁租体育用品平台设计

校园运动用品租借租赁租体育用品平台设计 摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 基于web的校园运动用品租借平台主要有管理员和用户两个角色功能模块&#xff0c;管理员对后台对有相…

Python 接口自动化中,如何实现参数化测试?

在Python接口自动化中&#xff0c;参数化测试是一种非常重要的技术&#xff0c;它可以帮助我们更高效地执行大量的接口测试用例。参数化测试允许我们通过修改测试数据&#xff0c;多次执行同一个测试用例&#xff0c;以验证系统在不同输入情况下的行为。下面&#xff0c;我将从…

[高频 SQL 50 题(基础版)]第一千七百五十七题,可回收且低脂产品

题目&#xff1a; 表&#xff1a;Products ---------------------- | Column Name | Type | ---------------------- | product_id | int | | low_fats | enum | | recyclable | enum | ---------------------- product_id 是该表的主键&#xff08;具有唯…

php简单商城小程序系统源码

&#x1f6cd;️【简单商城小程序】&#x1f6cd;️ &#x1f680;一键开启&#xff0c;商城搭建新体验&#x1f680; 你还在为繁琐的商城搭建流程头疼吗&#xff1f;现在&#xff0c;有了简单商城系统小程序&#xff0c;一切变得轻松又快捷&#xff01;无需复杂的编程知识&a…

羊大师:暑期不“胖”秘籍:羊奶滋养,细嚼慢咽是关键!

夏日炎炎&#xff0c;假期悠长&#xff0c;如何在享受悠闲时光的同时&#xff0c;保持轻盈体态&#xff0c;成了许多人心中的小秘密。今天&#xff0c;就让我们一起揭秘暑期不“胖”的秘籍&#xff0c;让羊奶的滋养与细嚼慢咽的智慧&#xff0c;成为你美丽夏日的守护神。 羊奶轻…

BUUCTF[堆][of_by_one]

堆中of_by_one 介绍&#xff1a; 严格来说 off-by-one 漏洞是一种特殊的溢出漏洞&#xff0c;off-by-one 指程序向缓冲区中写入时&#xff0c;写入的字节数超过了这个缓冲区本身所申请的字节数并且只越界了一个字节。溢出字节为可控制任意字节 &#xff1a;通过修改大小(size…

一个项目学习Vue3---事件处理

学习下面代码&#xff0c;了解Vue3的事件处理 <!--条件和列表渲染--> <template><el-button v-on:click"countAdd" type"primary">count{{ count }}</el-button><el-button click"countAdd" type"primary"…

18.按键消抖模块设计(使用状态机,独热码编码)

&#xff08;1&#xff09;设计意义&#xff1a;按键消抖主要针对的时机械弹性开关&#xff0c;当机械触点断开、闭合时&#xff0c;由于机械触点的弹性作用&#xff0c;一个按键开关在闭合时不会马上稳定地接通&#xff0c;在断开时也不会一下子就断开。因而在闭合以及断开的瞬…

解决centos yum和wget指令都用不了(换源)

先ping一下网络&#xff0c;看看能不能ping通&#xff0c;先排除是网络的问题 ping www.baidu.com有消息回传说明网络有连接&#xff0c;没有的话就要去把虚拟机的网络连接好&#xff08;CtrlC关闭&#xff09; 接下来写centos更换源 一般yum用不了&#xff0c;下载不来wget …

树莓派采集系统

树莓派&#xff08;Raspberry Pi&#xff09;是一款非常受欢迎的小型单板计算机&#xff0c;因其低成本、低功耗以及丰富的I/O接口&#xff0c;非常适合用来搭建数据采集系统。无论是环境监测、智能家居、工业自动化&#xff0c;还是科学实验&#xff0c;树莓派都能胜任。以下是…

苹果开发者取消自动续费

文档&#xff1a;https://support.apple.com/zh-cn/118428 如果没有找到订阅&#xff0c;那就是账号不对 取消订阅后&#xff0c;就不会自动续费了&#xff0c;如果不放心&#xff0c;可以把付款绑定的方式也取消

window 安装 openssl

文章目录 前言window 安装 openssl1. 下载2. 安装3. 配置环境变量4. 测试 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话…

虚幻引擎中增强输入映射中鼠标输入无反应,怎么办?

在本地重新实现官方示例Cropout的时候&#xff0c;有增强输入&#xff0c;设置了鼠标左键的输入操作&#xff0c;但是蓝图一直不能触发 鼠标输入无效&#xff0c;而改用键盘输入则能正常触发。 解决办法就是在蓝图中设置输入模式游戏和UI 则鼠标左键控制的IA_Villager可以正…

Mybatis实现RBAC权限模型查询

RBAC权限模型 Role-Based Access Control&#xff0c;中文意思是&#xff1a;基于角色&#xff08;Role&#xff09;的访问控制。这是一种广泛应用于计算机系统和网络安全领域的访问控制模型。 简单来说&#xff0c;就是通过将权限分配给➡角色&#xff0c;再将角色分配给➡用…

凝思安全操作系统安装部署

原文链接&#xff1a;凝思安全操作系统安装部署 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于凝思安全操作系统安装部署的文章。凝思安全操作系统是一款注重安全和隐私保护的操作系统&#xff0c;适用于各种高安全性需求的场景。本文将详细介绍如何安装和部署…

阿里云人工智能平台PAI部署开源大模型chatglm3之失败记录--update:最后成功了!

想学习怎么部署大模型&#xff0c;跟着网上的帖子部署了一个星期&#xff0c;然而没有成功。失败的经历也是经历&#xff0c;记在这里。 我一共创建了3个实例来部署chatglm3&#xff0c;每个实例都是基于V100创建的&#xff08;当时没有A10可选了&#xff09;&#xff0c;其显…