Redis学习笔记-003

news2024/11/24 9:27:57

Redis企业实战—基于Redis短信验证功能

文章目录

  • Redis企业实战---基于Redis短信验证功能
  • 一、短信登录实现
    • 1.1、导入[黑马点评项目](https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwd=eh11)
    • 1.2、基于Session实现
    • 1.3、集群的session的共享问题
    • 1.4、基于Redis实现共享session登录

Redis可以进行如下操作

在这里插入图片描述

一、短信登录实现

1.1、导入黑马点评项目

表的基本信息
在这里插入图片描述
前后端分离
在这里插入图片描述
导入后端项目
在这里插入图片描述
导入前端项目

在这里插入图片描述
运行前端项目

在这里插入图片描述

1.2、基于Session实现

实现流程
在这里插入图片描述
发送验证码
在这里插入图片描述
发送验证码

主要进行如下操作:

  • 对手机号进行校验
  • 模拟生成一个验证码并保存在session中
 @Override
    public Result sendCode(String phone, HttpSession session) {
        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone))
        {
            // 2.不符合就返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 3. 符合,生成验证码--一般长度为6位  用一个随机数默认向手机号发送短信
        String code = RandomUtil.randomNumbers(6);
        // 4.将验证码保存到session中
        session.setAttribute("code",code);
        // 5.发送验证码--aliyun的第三方库
        log.debug("验证码发送成功,验证码:"+code);
        // 5.返回ok
        return Result.ok();
    }

登录

主要进行如下操作:

  • 通过数据绑定获取到前端的表单数据
  • 对手机号进行校验
  • 对验证码进行检验看是否和存在session中的验证码一致
  • 按手机号对user表进行查询
  • 没有当前用户,就创建为新用户并保存到session中
 @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {

        String phone = loginForm.getPhone();

        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 不符合就返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 2.继手机号之后,校验验证码
        String code = loginForm.getCode();
        String cacheCode = (String) session.getAttribute("code");
        if (cacheCode == null || RegexUtils.isCodeInvalid(code) || cacheCode == code) {
            // 3.不一致,报错
            return Result.fail("验证码错误!");
        }

        // 4.一致,根据手机号查询用户
        User user = query().eq("phone", phone).one();

        // 5.判断用户是否存在
        if (user == null){
            // 6.不存在,创建新用户并保存
             user = createUserWithPhone(phone);
        }


        session.setAttribute("user",user);
        //  每一个session都有一个SessionID
        return Result.ok();
    }

    private User createUserWithPhone(String phone) {
        // 1. 创建用户
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));
        // 2.保存用户
        save(user);
        return user;

    }

登录验证功能
在这里插入图片描述

定义拦截器

主要进行如下操作:

  • 获取session中的用户
  • 判断用户是否存在,
  • 存在,保存用户信息到 ThreadLocal
  • 放行
public class LoginInterceptor implements HandlerInterceptor {

    // 1. 前置拦截器
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 获取session
        HttpSession session = request.getSession();
        // 2. 获取session中的用户
        Object user = session.getAttribute("user");
        // 3. 判断用户是否存在
        if (user == null){
            // 4. 不存在,拦截,返回401代码
            response.setStatus(401);
            return false;
        }
        // 5. 存在,保存用户信息到 ThreadLocal
        UserHolder.saveUser((User) user);
        // 6. 放行
        return true;
    }



    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

配置拦截器

主要进行如下操作:

  • 将定义的LoginInterceptor登录拦截器添加进去
  • 并排除一些不需要拦截的请求
@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    // 将定义的LoginInterceptor登录拦截器添加进去
  	// 并排除一些不需要拦截的请求
        registry.addInterceptor(new LoginInterceptor())
                .excludePathPatterns(
                        "/shop/**",
                        "/shop-type/**",
                        "/voucher/**",
                        "/upload/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                );
    }
}

1.3、集群的session的共享问题

session实现共享所面临的问题:
在这里插入图片描述
Redis代替session需要考虑的问题

  • 选择合适的数据结构
  • 选择合适的key
  • 选择合适粒度

短信验证和登录注册
在这里插入图片描述
验证码发送

主要进行如下操作:

  • 检验手机号是否合法
  • 生成一个模拟验证码,并将验证码保存到redis中
  • 给保证验证码的key设置有效期
  @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //引入ObjectMapperJSON处理类
    @Override
    public Result sendCode(String phone, HttpSession session) {
        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone))
        {
            // 2.不符合就返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 3. 符合,生成验证码--一般长度为6位  用一个随机数默认向手机号发送短信
        String code = RandomUtil.randomNumbers(6);
        // 4.将验证码保存到Redis中 --- 一般加个前缀即能保证唯一性,又能指明当前key-value的功能
        // 5.设置验证码的key的有效期  LOGIN_CODE_KEY:前缀  LOGIN_CODE_TTL:有效期时间
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);

        // 6.发送验证码--aliyun的第三方库
        log.debug("验证码发送成功,验证码:"+code);
        // 7.返回ok
        return Result.ok();
    }

校验登录状态
在这里插入图片描述
登录凭证token的存储方式
在这里插入图片描述
登录代码

主要操作分为以下几点:

  • 获取表单的数据,进行手机号的验证
  • 从redis中读取保存的验证码(前缀+手机号为key),并判断是否正确
  • 当数据符合要求时,利用手机号进行查询user表
  • 当没有数据的时候,进行创建新用户并保存
  • 保存用户信息到redis
    • 用随机token 作为登录令牌
    • 将Java对象转换为Hash进行存储
    • 设置token有效期
@Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {

        String phone = loginForm.getPhone();

        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 不符合就返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // TODO 2.从redis获取到验证码并进行验证
        String code = loginForm.getCode();
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY+phone);
        if (cacheCode == null || RegexUtils.isCodeInvalid(code) || cacheCode == code) {
            // 3.不一致,报错
            return Result.fail("验证码错误!");
        }

        // 4.一致,根据手机号查询用户
        User user = query().eq("phone", phone).one();

        // 5.判断用户是否存在
        if (user == null){
            // 6.不存在,创建新用户并保存
             user = createUserWithPhone(phone);
        }

        // TODO 7. 保存用户信息到redis,问题进行保存的只能是用户的部分信息,不能包括完整信息,用户的敏感数据必须被隐藏。UserDTD类只包含数据的部分信息
        // 7.1、用随机token 作为登录令牌
        String token = UUID.randomUUID().toString(true);
        //  7.2、将Java对象转换为Hash进行存储
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        Map<String, Object> map = BeanUtil.beanToMap(userDTO);
        String key = LOGIN_USER_KEY+token;
        map.put("id",userDTO.getId().toString());
        stringRedisTemplate.opsForHash().putAll(key,map);
        //  7.3、设置token有效期 30分钟 CACHE_SHOP_TTL常量
        stringRedisTemplate.expire(key,CACHE_SHOP_TTL,TimeUnit.MINUTES);
        return Result.ok(token);
    }

拦截器配置

主要获得请求头携带的token(登录动作的返回值,在前端作为数据存储在请求头中),方便后续的操作。主要操作:

  • 创建构造函数,方便MvcConfig 注入StringRedisTemplate对象
  • 获取请求头中的token
  • 基于token获取reids中的用户
  • 判断用户是否为空
  • 将查询到的hash数据转为UserDTD对象
  • 存在,保存用户信息到 ThreadLocal
  • 刷新token的有效期
  • 放行
public class LoginInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    // 1. 前置拦截器
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 获取请求头中的token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)){
            //  不存在,拦截,返回401代码
            response.setStatus(401);
            return false;
        }
        // 2. 基于token获取reids中的用户
        Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(LOGIN_USER_KEY + token);
        // 3. 判断是否是空值
        if (map.isEmpty()){
            // 4. 不存在,拦截,返回401代码
            response.setStatus(401);
            return false;
        }
        // 5. 将查询到的hash数据转为UserDTD对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(map, new UserDTO(), false);
        // 6. 存在,保存用户信息到 ThreadLocal
        UserHolder.saveUser(userDTO);
        // 7. 刷新token的有效期
        String key = LOGIN_USER_KEY + token;
        stringRedisTemplate.expire(key,30, TimeUnit.MINUTES);
        // 8. 放行
        return true;
    }



    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

注入拦截器

将定义的LoginInterceptor登录拦截器注入到spring中,需要进行以下操作:

  • 排序一些不需要拦截的请求
  • 注入StringRedisTemplate 对象
@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 排除一些不需要拦截的请求
        registry.addInterceptor(new LoginInterceptor(stringRedisTemplate))
                .excludePathPatterns(
                        "/shop/**",
                        "/shop-type/**",
                        "/voucher/**",
                        "/upload/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                );
    }
}

1.4、基于Redis实现共享session登录

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

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

相关文章

【算法】排序——插入排序及希尔排序

目录 前言 一、排序的概念及其应用 1.1排序的概念 1.2排序的应用 1.3常见的排序算法 二、插入排序的实现 基于插入排序的优化——希尔排序&#xff08;缩小增量排序 个人主页 代码仓库 C语言专栏 初阶数据结构专栏 Linux专栏 LeetCode刷题 算法专栏 前言 这…

Tensorflow1架构内核和学习方法论

目录 概念简介 总体介绍 名词解释 疑问辨析 工程构建 代码生成 技术栈 模型类型 系统架构 分层架构 图控制 运行机制 会话机制 队列 运行模型 本地模式 分布式模式 技能方法论 发现领域模型 挖掘系统架构 细节是魔鬼 适可而止 发现她的美 形式化 独…

代码随想录算法训练营 60天总结

emmmm,总结怎么写呢。 暑假和高中一个同学聊天&#xff0c;因为都参加了蓝桥杯&#xff0c;我连省三也没有&#xff0c;同学竟然省一然后去北京参加国赛获得国三&#xff0c;就问问他学习编程的方法。他推荐我加入了知识星球里的代码随想录&#xff0c;里面有学习路线还有好多讨…

LVS: ambighouse pin count in file “xx“ but none has xx pins问题

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 一些foundry的standard cell的cdl没有衬底pin&#xff08;例如VNW VPW&#xff09;&#xff0c;如果pr过程中globalNetConnect 或者connect_pg_net把衬底pin接到power/ground n…

Qt5开发及实例V2.0-第二十二章-Qt.Quick Controls 2新颖界面开发

Qt5开发及实例V2.0-第二十二章-Qt.Quick Controls 2新颖界面开发 第22章 Qt Quick Controls 2新颖界面开发22.1 Qt Quick Controls 2简介22.1.1 第一个Qt Quick Controls 2程序22.1.2 Qt Quick Controls 2程序的构成 22.2 Qt Quick Controls 2与1的比较22.2.1 ApplicationWindo…

ElementUI实现登录注册+axios全局配置+CORS跨域

一、搭建项目 1.1 安装 Element-UI 先确保是否安装了vue-cli脚手架工具 !!! 安装vue脚手架可以看看我的上一篇博客 构建好项目后通过npm安装element-ui cd 项目根路径 #进入新建项目的根目录 npm install element-ui -S #安装…

MySQL简介以及安装和部署(Linux)

MySQL简介 MySQL是一个小型关系数据库管理系统&#xff0c;开发者为瑞典MySQL AB公司。在2008年1月16号被sun公司10亿美金收购。2009年&#xff0c;SUN又被Oracle以74亿美金收购。 目前MySQL被广泛地应用在Internet上的中小型网站中。由于体积小、速度快、总体拥有成本低&…

C#__简单使用TCP/UDP发送消息

Socket(套接字、插口) TCP和UCP的区别&#xff1a; 1、基于连接和无连接 2、对系统资源的要求&#xff08;TCP较多&#xff0c;UCP少&#xff09; 3、UDP程序结构简单 4、流模式和数据报模式 5、TCP保证数据正确性和数据先后顺…

【腾讯云国际站】CDN内容分发网络特性介绍

为什么使用腾讯云国际站 CDN 内容分发网络&#xff1f; 当用户直接访问源站中的静态内容时&#xff0c;可能面临的体验问题&#xff1a; 客户离服务器越远&#xff0c;访问速度越慢。客户数量越多&#xff0c;网络带宽费用越高。跨境用户访问体验较差。 腾讯云国际站CDN 如何改…

yum和vim工具的使用

目录 yum工具的使用 yum下载原理 软件的查找&下载&删除操作 查找lrzsz软件&#xff08;文件上传或者下载软件&#xff09; 下载lrzsz软件 删除lrzsz软件 vim工具的使用 vim命令模式 命令模式与光标相关的快捷键&#xff1a; 插入模式 底行模式 在本次的博客当中我们主要…

C#/Unity3D 单例模板(单例属性模板)

C# 单例单例属性 不做过多解释&#xff0c;非面向大众 using System; namespace EasyAVG {public static class SingletonProperty<T> where T : class{private static readonly object locker new object();private volatile static T instance null;public static…

一篇文章成为递归大神:MySQL递归查询(with recursive)

理论原理 1、MySQL with Recursive是什么&#xff1f; MySQL with Recursive是一种基于递归思想的MySQL查询方式&#xff0c;可以实现对数据的递归查询和处理&#xff0c;返回符合条件的数据。在MySQL 8.0版本中&#xff0c;该功能被正式引入。 2、MySQL with Recursive有什么…

【100天精通Python】Day65:Python可视化_Matplotlib3D绘图mplot3d,绘制3D散点图、3D线图和3D条形图,示例+代码

1 mpl_toolkits.mplot3d 功能介绍 mpl_toolkits.mplot3d 是 Matplotlib 库中的一个子模块&#xff0c;用于绘制和可视化三维图形&#xff0c;包括三维散点图、曲面图、线图等。它提供了丰富的功能来创建和定制三维图形。以下是 mpl_toolkits.mplot3d 的主要功能和功能简介&am…

xxe攻击(XML外部实体)

1.定义 XML用于标记电子文件使其具有结构性的标记语言&#xff0c;可以用来标记数据、定义数据类型&#xff0c;是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义&#xff08;可选&#xff09;、文档元素。 http://www.w3school.com.…

Spring学习笔记9 SpringIOC注解式开发

Spring学习笔记8 Bean的循环依赖问题_biubiubiu0706的博客-CSDN博客 注解的存在主要是为了简化XML的配置.Spring6倡导全注解式开发 回顾下 注解怎么定义,注解中的属性怎么定义 注解怎么使用 通过反射机制怎么读取注解 注解的自定义 注解的使用 通过反射机制怎么读取注解 I…

顺序表的实现和练习

杂谈&#xff1a; 有些数据结构&#xff08;C语言实现&#xff09;的教材/教程中会使用C中引用的语法&#xff0c;引用确实在形式上比指针简洁&#xff0c;这样做无非是为了避免后续对二级指针的使用。 我认为既然使用C语言实现数据结构&#xff0c;那么指针就不应该是门槛。…

【动手学深度学习-Pytorch版】序列到序列的学习(包含NLP常用的Mask技巧)

序言 这一节是对于“编码器-解码器”模型的实际应用&#xff0c;编码器和解码器架构可以使用长度可变的序列作为输入&#xff0c;并将其转换为固定形状的隐状态&#xff08;编码器实现&#xff09;。本小节将使用“fra-eng”数据集&#xff08;这也是《动手学习深度学习-Pytor…

[论文分享] How to Better Utilize Code Graphs in Semantic Code Search?

How to Better Utilize Code Graphs in Semantic Code Search? [ESEC/FSE 2022] 语义代码搜索极大地促进了软件的重用&#xff0c;使用户能够找到与用户指定的自然语言查询高度匹配的代码片段。由于代码图(如控制流图和程序依赖图)丰富的表达能力&#xff0c;两种主流的研究工…

【Gradle-9】Gradle插件发布指南

1、前言 不管是在公司内部&#xff0c;还是开源&#xff0c;Gradle插件发布都是一项必备的技能&#xff0c;本文主要介绍本地发布和远端发布两种方式。 2、本地发布 2.1、添加依赖 在plugin>build.gradle文件中&#xff08;插件的项目&#xff09;先依赖一个maven发布的…

分布式搜索引擎Elasticsearch

一、Elasticsearch介绍 1.Elasticsearch产生背景 大数据量的检索NoSql: not only sql,泛指非关系型的数据库Nginx的7层负载均衡和4层负载均衡2.Elasticsearch是什么 一个基于Lucene的分布式搜索和分析引擎,一个开源的高扩展的分布式全文检索引擎 Elasticsearch使用Java开发…