短信登录的实现-redis和session的比较

目录

  • 短信登录功能的实现
    • 一:基于session进行短信登录
    • 1:发送验证码
    • 2:登录
    • 3:登录验证拦截器
    • 4:隐藏用户敏感信息
    • 二:session的集群共享问题
    • 三:基于redis实现短信登录
      • 登录的刷新问题

短信登录功能的实现

一:基于session进行短信登录

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1:发送验证码

@Override
public Result sendCode(String phone, HttpSession session) {//使用工具类判断手机号是否有效if (RegexUtils.isPhoneInvalid(phone)) {//无效返回错误信息return Result.fail("手机号格式错误");}//使用hutool包中的生成随机数的方法生成一个6位的验证码String code = RandomUtil.randomNumbers(6);//向返回的session中添加验证码信息session.setAttribute("code",code);//模拟发送验证码log.debug("验证码发送成功,{}",code);//发送成功返回return Result.ok();}

2:登录

@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {//检查手机号是否有效,因为可能发送验证码时是正确的,后面又更改了if (RegexUtils.isPhoneInvalid(loginForm.getPhone())) {return Result.fail("手机号格式错误");}//进行验证码的校验String code = (String) session.getAttribute("code");//获取session中的验证码String code1 = loginForm.getCode();//获取用户输入的验证码if (code1==null||!code1.equals(code)){//比较是否相同return Result.fail("验证码输入错误");}//查找用户,看是否存在,从而判断是注册还是登录String phone = loginForm.getPhone();//获取手机号User user = lambdaQuery().eq(User::getPhone, phone).one();//通过mp进行单表查询,条件是手机号相同if (user==null){user= createUserWithPhone(phone);//用户为空,说明是注册,我们调用一个自定义方法来保存返回这个用户}//将用户保存到session中session.setAttribute("user",user);return Result.ok();
}private User createUserWithPhone(String phone) {User user = new User();//创建用户user.setPhone(phone);user.setNickName("user_"+RandomUtil.randomString(6));//获取一个随机用户名save(user);//mp方法直接保存return user;
}

校验验证码-》查询用户-》注册/登录-》保存到session

3:登录验证拦截器

1:自定义拦截器

//自定义的拦截器要实现HandlerInterceptor接口
public class LoginInterceptor implements HandlerInterceptor {//HandlerInterceptor中有三个可以重写的方法:拦截前处理,中,后@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取sessionHttpSession session = request.getSession();//获取session中的用户User user = (User) session.getAttribute("user");//判断用户是否存在if (user==null){//用户不存在不放行,返回状态码401:权限不足response.setStatus(401);return false;}//属性拷贝user->dtoUserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);//将用户信息放置到线程中;UserHolder.saveUser(userDTO);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//放止内存泄露,拦截结束后将用户从线程中删除UserHolder.removeUser();}
}

2:注册拦截器:

//配置类加上注解Configuration,然后注册拦截器就要使用WebMvcConfigurer中的方法,所以我们先实现了
@Configuration
public class MVCConfig implements WebMvcConfigurer {@Override//添加拦截器的方法public void addInterceptors(InterceptorRegistry registry) {//添加拦截器registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login");//指定不许拦截的路径}
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4:隐藏用户敏感信息

//转成dto隐藏用户信息
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);

二:session的集群共享问题

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因为session是存储在tomcat服务器中的,我们将来部署集群进行负载均衡,那么我们存储在一台tomcat服务器的数据无法实现共享;

我们考虑采用redis:1:共享数据;2:内存存储;3:键值结构

三:基于redis实现短信登录

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们以手机号为key,验证码为value保存到redis,然后后面将用户保存到redis使用hash来保存;

用户信息的key使用一个token来保存,一个是保证key的唯一性,一个是保证数据的安全性,因为token也就是key是要存在浏览器中的;要保证安全性;

我们做的修改:

在发送验证码时:

@Override
public Result sendCode(String phone, HttpSession session) {//使用工具类判断手机号是否有效if (RegexUtils.isPhoneInvalid(phone)) {//无效返回错误信息return Result.fail("手机号格式错误");}//使用hutool包中的生成随机数的方法生成一个6位的验证码String code = RandomUtil.randomNumbers(6);//session.setAttribute("code",code);//将验证码保存到redis中,手机号为key,验证码为value,并且设置有效期为2分钟redisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY+phone,code,2, TimeUnit.MINUTES);//模拟发送验证码log.debug("验证码发送成功,{}",code);//发送成功返回return Result.ok();}

在登录验证时:

 @Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//检查手机号是否有效,因为可能发送验证码时是正确的,后面又更改了if (RegexUtils.isPhoneInvalid(loginForm.getPhone())) {return Result.fail("手机号格式错误");}//进行验证码的校验
//        String code = (String) session.getAttribute("code");String code1 = loginForm.getCode();//获取用户输入的验证码String phone = loginForm.getPhone();//获取手机号String code = redisTemplate.opsForValue().get(phone);if (code1==null||!code1.equals(code)){//比较是否相同return Result.fail("验证码输入错误");}//查找用户,看是否存在,从而判断是注册还是登录User user = lambdaQuery().eq(User::getPhone, phone).one();//通过mp进行单表查询,条件是手机号相同if (user==null){user= createUserWithPhone(phone);//用户为空,说明是注册,我们调用一个自定义方法来保存返回这个用户}//转成dto隐藏用户信息UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
//        session.setAttribute("user",userDTO);//随机生成token,使用uuid生成,然后转成字符串String token = UUID.randomUUID().toString(true);//将对象转成map,使用beanutil的方法,用于后面给hash赋值Map<String, Object> map = BeanUtil.beanToMap(userDTO);//将用户存储在hash中,key为token,然后putall加入多个字段是map,我们前面创建过了;redisTemplate.opsForHash().putAll(RedisConstants.LOGIN_USER_KEY+token,map);//设置过期时间redisTemplate.expire(RedisConstants.LOGIN_USER_KEY+token,30,TimeUnit.MINUTES);return Result.ok(token);}

拦截器中的操作:

@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        HttpSession session = request.getSession();//从请求头中获取tokenString token = request.getHeader("authorization");//从redis中根据token取出map;Map<Object, Object> usermap = redisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);
//        UserDTO userDTO = (UserDTO) session.getAttribute("user");//判断map是否为空,不用判断是否为null,因为上面的方法entries会帮我们判断if (usermap.isEmpty()){//用户不存在不放行,返回状态码401:权限不足response.setStatus(401);return false;}//将map通过beanutil中的方法转成对象,fillBeanWithMap(usermap, new UserDTO(), false),第二个参数是//对象类型,第三个是是否抛出异常;UserDTO userDTO = BeanUtil.fillBeanWithMap(usermap, new UserDTO(), false);//将用户信息放置到线程中;UserHolder.saveUser(userDTO);//刷新token的有效值:只要用户一直访问token一直存在就不需要重新登录获取token,超过刷新时间就需要重新登录redisTemplate.expire(RedisConstants.LOGIN_USER_KEY+token,RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);return true;}

登录的刷新问题

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们之前只有一个拦截器,但是拦截的路径不是所有,如果用户一直访问的是不需要拦截的路径,那么他的token就不会刷新,就会失去登录状态,我们可以再加一个拦截器,第一个拦截一切路径,并且刷新有效期,第二个做登录校验;

我们重新定义一个拦截器,无论与否都放行:

将拦截校验的操作交给第二个拦截器,

 @Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        HttpSession session = request.getSession();//从请求头中获取tokenString token = request.getHeader("authorization");//从redis中根据token取出map;Map<Object, Object> usermap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);//判断map是否为空,不用判断是否为null,因为上面的方法entries会帮我们判断if (usermap.isEmpty()){//这个拦截器不做登录校验,直接放行;return true;}//将map通过beanutil中的方法转成对象,fillBeanWithMap(usermap, new UserDTO(), false),第二个参数是//对象类型,第三个是是否抛出异常;UserDTO userDTO = BeanUtil.fillBeanWithMap(usermap, new UserDTO(), false);//将用户信息放置到线程中;UserHolder.saveUser(userDTO);//刷新token的有效值:只要用户一直访问token一直存在就不需要重新登录获取token,超过刷新时间就需要重新登录stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY+token,RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);return true;}

第二个拦截器:他判断是否登录的依据就是treadlocal中有没有用户:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {UserDTO user = UserHolder.getUser();if (StrUtil.isBlankIfStr(user)) {response.setStatus(401);return false;}return true;
}

别忘记注册拦截器:

public void addInterceptors(InterceptorRegistry registry) {//添加拦截器registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login").order(1);//指定不许拦截的路径//order设置拦截器的先后顺序,order的值越小,拦截器越先执行;registry.addInterceptor(new ReFlashTokenINterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);}

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

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

相关文章

中欧科学家论坛暨第六届人工智能与先进制造国际会议(AIAM 2024)在德国法兰克福成功举办,两百余名中外科学家共襄盛举

2024年10月20至21日&#xff0c;首届中欧科学家论坛在德国法兰克福的SAALBAU Titus Forum国际会议中心成功举行。中国驻法兰克福总领事馆伍鹏飞副总领事、德国兰斯巴赫-鲍姆巴赫市市长Michael Merz亲自出席并致辞。2004年诺贝尔化学奖得主Aaron Ciechanover教授和法国国家科学院…

直接删除Github上的文件

直接删除Github上的文件 说明&#xff1a;此操作只删除Github上的文件&#xff0c;本地仓库文件不受影响 1.确定要删除哪个分支文件,以删除main为例&#xff0c; 1.找到本地仓库位置以StudyNote为例&#xff0c;右键 bash here 2.打开命令窗口&#xff0c;将Github的StudyN…

SpringBoot篇(运维实用篇 - 日志)

目录 一、简介 二、代码中使用日志工具记录日志 1. 操作步骤 步骤1&#xff1a;添加日志记录操作 步骤2&#xff1a;设置日志输出级别 步骤3&#xff1a;设置日志组 2. 知识小结 三、优化日志对象创建代码 1. 实例 2. 总结 四、日志输出格式控制 1. 实例 2. 总结 …

Java多线程编程基础

目录 编写第一个多线程程序 1. 方式一 : 继承Thread类, 重写run方法 2. 方式二: 实现Runnable接口, 重写run方法 3. 方式三: 使用Lambda表达式 [匿名内部类] [Lambda表达式] 在上个文章中, 我们了解了进程和线程的相关概念. 那么, 在Java中, 我们如何进行多线程编程呢? …

postman的脚本设置接口关联

pm常用的对象 变量基础知识 postman获取响应结果的脚本的编写 下面是购物场景存在接口信息的关联 登录进入---搜索商品---进入商品详情---加入购物车 资源在附件中&#xff0c;可以私聊单独发送 postman的SHA256加密 var CryptoJS require(crypto-js);// 需要加密的字符串 …

ip地址分为几大类-IP和子网掩码对照表

一、IP地址的基本概念与分类 IP地址是用于在网络中标识每个设备的逻辑地址。互联网协议将IP地址分为A、B、C、D和E五类&#xff0c;其中A、B、C三类最常用&#xff0c;它们主要根据地址的首位位数以及用途进行划分。 A类地址&#xff1a; 范围&#xff1a;0.0.0.0 - 127.255.2…

docker占用磁盘过多问题

我在windows系统上用docker&#xff0c;安装在C盘环境下&#xff0c;我发现C盘占用了大量的空间&#xff0c;查找后发现是docker的映像文件占用的&#xff0c;于是开始清理&#xff0c;中间还踩个坑&#xff0c;记录一下&#xff0c;下次需要的时候方便找。 踩坑 我本想移动映…

Xss_less靶场攻略(1-18)

xss-lab-less1 ur特殊字符转义 存在url中 转义符为 %2B& 转义符为 %26空格 转义符为 或 %20/ 转义符为 %2F? 转义符为 %3F% 转义符为 %25#转义符为 %23 转义符为 %3Dimg 标签懒加载 在XSS攻击中&#xff0c;img标签的src属性是一个常见的攻击向量&#xff0c;因为它可以…

聊聊Web3D 发展趋势

随着 Web 技术的不断演进&#xff0c;Web3D 正逐渐成为各行业数字化的重要方向。Web3D 是指在网页中展示 3D 内容的技术集合。近年来&#xff0c;由于 WebGL、WebGPU 等技术的发展&#xff0c;3D 内容已经能够直接在浏览器中渲染&#xff0c;为用户提供更加沉浸、互动的体验。以…

【传知代码】图像处理解决种子计数方法

文章目录 一、背景及意义介绍研究背景农业考种需求传统计数方法的局限性人工计数仪器设备计数 研究意义提高育种效率提高计数准确性广泛的适用性数据存档与分析便利 二、概述三、材料与数据准备以及方法介绍整体流程图像采集图像预处理形态学操作腐蚀运算开运算 图像二值化种子…

uniapp开发【点击展示弹窗功能】

一、效果展示 二、代码 <template><view class="mini"><view class="block_item" @click="$refs.popup.op

centos7.X zabbix监控参数以及邮件报警和钉钉报警

1&#xff1a;zabbix安装 1.1 zabbix 环境要求 硬件配置: 2个CPU核心, 4G 内存, 50G 硬盘&#xff08;最低&#xff09; 操作系统: Linux centos7.2 x86_64 Python 2.7.x Mariadb Server ≥ 5.5.56 httpd-2.4.6-93.el7.centos.x86_64 PHP 5.4.161.2 zabbix安装版本 [rootnod…

根据输入的详细地址解析经纬度

摘要&#xff1a; 今天遇到一个需求&#xff1a;就是做客户导入的时候因为导入的客户地址的时候没有经纬度的&#xff0c;但是同步的时候需要经纬度的&#xff0c;所以还是要根据客户提供的详细地址解析出来对应的经纬度&#xff01;回填到对应的经纬度的表单之中进行客户的同步…

upload-labs靶场Pass-21

upload-labs靶场Pass-21 本关上传方法众多&#xff0c;但是应该考察的是数组后缀绕过&#xff0c;所以我的上传围绕此展开 1.分析源码 $is_upload false; // 初始化上传状态为false $msg null; // 初始化消息变量为null// 检查是否有文件上传 if(!empty($_FILES[upload_fi…

Flutter实战短视频课程

1、课程导学 一套代研运行多蜡 体州一致&#xff0c;目胜能优昇 未来大趋势 不改交原生项目的基础上&#xff0c;扩展Flutter能力 Flutter原生灵话切涣 0入侵 最简单、最通用 最新Flutter 3,x新特性讲解 大量flutter官方组件和api学习 最常用的第三方库使用及原理解析 自研组…

Python中的数据可视化:Matplotlib基础与高级技巧

Python中的数据可视化&#xff1a;Matplotlib基础与高级技巧 数据可视化是数据分析和数据科学中不可或缺的一部分。通过图表&#xff0c;我们可以更直观地观察数据的分布和趋势。Matplotlib作为Python最基础、也是最广泛使用的绘图库之一&#xff0c;不仅支持多种常用图表&…

无人机避障——使用三维PCD点云生成的2D栅格地图PGM做路径规划

着重介绍通过对三维 PCD 点云进行处理生成 2D 栅格地图 PGM&#xff0c;而后将该 PGM 地图充分运用到无人系统路径规划之中&#xff0c;使得无人机能够依据此规划合理避开飞行路线上可能出现的障碍物。&#xff08;解决如何使用PGM的问题&#xff09; Hybrid A*算法 参考博客…

线性代数(1)——线性方程组的几何意义

线性代数的基本问题是求解个未知数的个线性方程&#xff1b; 例如&#xff1a;&#xff08;方程1&#xff09;。 在线性代数的第一讲中&#xff0c;我们从Row Picture、Column Picture、Matrix Picture三个角度来看这个问题。 上面的系统是二维的。通过添加第三个变量&#…

浮动+flex布局

一.浮动 1.介绍 2.效果 <style> .one{ width: 100px; height: 100px; background-color: red; float: left; } .two{ width: 200px; height: 200px; background-color: blue; float: right; } </style> </head> <body> <div class"one&quo…

没有对象来和我手撕红黑树吧

1. 红黑树的介绍 红黑树也是一种自平衡的二叉搜索树&#xff0c;在每一个节点增加了一个存储位来表示节点的颜色&#xff0c;可以是红色也可以是黑色&#xff0c;通过约束颜色来维持树的平衡&#xff0c;具有以下的性质&#xff1a; 每个节点不是红色就是黑色根节点为黑色如果…