springboot项目:瑞吉外卖 前后端 代码、思路 详细分析 part5

news2024/11/17 11:44:17

part1
part2
part3
part4
part5 本页

6 移动端短信发送和手机验证码登入

6.1 短信发送
6.2 手机验证码登入

6.1 短信发送

6.1.1整体分析

在这里插入图片描述
2.
在这里插入图片描述
3. 注册登入阿里云账户。找到短信服务,设置短信签名(上面图片的阿里云、菜鸟裹裹、天猫…),模板等等
在这里插入图片描述
4. 设置AccessKey
5. 看帮助文档,导入对应的Util包

6.2 手机验证码登入

6.2.1 整体思路整理

  1. 需求分析在这里插入图片描述
  2. 涉及表的操作(数据模型)
    在这里插入图片描述
  3. 代码开发思路
    在这里插入图片描述
    想要判断当前手机号是否在表中,如果不在,说明是新用户,将改号码保存
  4. 使用浏览器的手机模式查看H5的页面
    在这里插入图片描述
  5. 拦截器改造,现在加入移动端的登入(session中是否有user,user是成功登入后设置的),设置threadLocal等信息。
  6. 后端思路:短信发送的controller处理完之后,把发送的验证码存到session中。之后是登入的controller,把前端提交的短信验证码和session中的验证码进行比较,如果手机号和验证码都没错,那就放行(设置一个User的session,拦截器就会放行),同时查数据库,看看有没有对应的手机号,没有的话就插入这条手机号的数据

6.2.2 前端代码分析

  1. login.html显示到页面上的代码如下:
<div id="login" v-loading="loading">
    <div class="divHead">登录</div>
    <div class="divContainer">
        <el-input placeholder=" 请输入手机号码" v-model="form.phone"  maxlength='20'/></el-input>
        <div class="divSplit"></div>
        <el-input placeholder=" 请输入验证码" v-model="form.code"  maxlength='20'/></el-input>
        <span @click='getCode'>获取验证码</span>
    </div>
    <div class="divMsg" v-if="msgFlag">手机号输入不正确,请重新输入</div>
    <el-button type="primary" :class="{btnSubmit:1===1,btnNoPhone:!form.phone,btnPhone:form.phone}" @click="btnLogin">登录</el-button>
</div>
  1. 获取验证码,注意发送请求的参数是sendMsgApi({phone:this.form.phone}),json的格式,后端使用注解@RequestParam接收,可以直接接收到一个对象中,属性含有phone字段
getCode(){
    this.form.code = ''
    //正则表达式验证手机号是否正确
    const regex = /^(13[0-9]{9})|(15[0-9]{9})|(17[0-9]{9})|(18[0-9]{9})|(19[0-9]{9})$/;
    if (regex.test(this.form.phone)) {
        this.msgFlag = false
        //sendMsgApi({phone:this.form.phone}) 一个json,key是phone,value是前端输入的手机号
        this.form.code = (Math.random()*1000000).toFixed(0)
    }else{
        this.msgFlag = true
    }
},

这里的msgFlag作用是对应之前的:<div class="divMsg" v-if="msgFlag">手机号输入不正确,请重新输入</div>,即已经通过了验证,不需要弹出提示信息
正常情况下,通过sendMsgApi发送axios请求,请求到后端发送验证码

function sendMsgApi(data) {
    return $axios({
        'url': '/user/sendMsg',
        'method': 'post',
        data
    })
}
  1. 登入模块,前端只需要给后端传loginApi(this.form),这个form:{phone:‘’“,code:‘’” } 是一个json,需要把phone和前端的code都传给后端,后端之前设置了session:session.setAttribute(phone,code); 进行一个验证就好了。
async btnLogin(){
    if(this.form.phone && this.form.code){
        this.loading = true
        const res = await loginApi(this.form)
        this.loading = false
        if(res.code === 1){
            sessionStorage.setItem("userPhone",this.form.phone)
            window.requestAnimationFrame(()=>{
                window.location.href= '/front/index.html'
            })                           
        }else{
            this.$notify({ type:'warning', message:res.msg});
        }
    }else{
        this.$notify({ type:'warning', message:'请输入手机号码'});
    }
}

6.2.3 后端代码分析

整体思路:短信发送的controller处理完之后,把发送的验证码存到session中。之后是登入的controller,把前端提交的短信验证码和session中的验证码进行比较,如果手机号和验证码都没错,那就放行(设置一个User的session,拦截器就会放行),同时查数据库,看看有没有对应的手机号,没有的话就插入这条手机号的数据

  1. 发送短信controller: 前端发送请求的参数是sendMsgApi({phone:this.form.phone}),json的格式,后端使用注解@RequestParam接收,可以直接接收到一个对象中,属性含有phone字段
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session){
    //获取手机号
    String phone = user.getPhone();

    if(StringUtils.isNotEmpty(phone)){
        //生成随机的4位验证码
        String code = ValidateCodeUtils.generateValidateCode(4).toString();
        log.info("code={}",code);

        //调用阿里云提供的短信服务API完成发送短信
        //SMSUtils.sendMessage("瑞吉外卖","",phone,code);

        //需要将生成的验证码保存到Session
        session.setAttribute(phone,code);

        return R.success("手机验证码短信发送成功");
    }

    return R.error("短信发送失败");
}
  1. 注意这里不能再使用User接收,主要是User中没有code验证码属性,解决方法一是使用Dto继承User,方法二是使用map接收。这里用方法二,因为code验证码只需要和session做验证就好了,没有存到数据库里的需求。
/**
     * 登入验证
     * @param map 前端传过来电话和验证码,User没有验证码的属性,接不住,所以用map,也可以用Dto
     * @param session 验证成功了,就把user存入session,拦截器那边放行
     * @return
     */
    @PostMapping("/login")
    public RetObj loginController(@RequestBody Map<String,String> map, HttpSession session){
        String phone = map.get("phone");
        String code = map.get("code");
        String sessionCode = session.getAttribute(phone).toString();
        if (StringUtils.isNotBlank(sessionCode) && code.equals(sessionCode)){
            //用户名秘密验证成功!
            //先查电话号码在不在数据库里,如果不在,就要保存
            LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(User::getPhone,phone);
            User user = userService.getOne(lambdaQueryWrapper);
            if (user ==  null){
                //存入数据库
                User user1 = new User();
                user1.setPhone(phone);
                user1.setStatus(1);
                userService.save(user1);
                //session.setAttribute("user",user1.getId());
            }else {
                session.setAttribute(Contants.SESSION_USERID,user.getId());
            }
            return RetObj.success("登入成功!");
        }else {
            return RetObj.error("登入失败!");
        }
  1. 写完之后配置拦截器,有两个类要配置
package cn.edu.uestc.ruijitakeout.common.interceptor;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String uri = request.getRequestURI();
        log.info("当前路径:{}", uri);

        /**
         * HandlerMethod=>Controller中标注@RequestMapping的方法
         *  需要配置静态资源不拦截时,添加这块逻辑  => 前后端分离项目
         *
         */
        // 是我们的controller中的方法就拦截,如果不是的话,放行,给加载静态资源
        if (!(handler instanceof HandlerMethod)) {
            log.info("是静态资源或非controller中的方法,放行");
            return true;
        }

        //1-通过session判断是否登入
        if (request.getSession().getAttribute(Contants.SESSIONLOGIN) != null) {
            log.info("用户已经登入,id={}", request.getSession().getAttribute(Contants.SESSIONLOGIN));

            //进入拦截器后,给threadLocal绑定session,让后面需要的公共字段自动填充的时候,填充这个updateUser
            //每次http请求,会分配一个新的线程来处理
            BaseContext.setThreadLocal((Long) request.getSession().getAttribute(Contants.SESSIONLOGIN));
            log.info("拦截器这里设置了ThreadLocal,值为:{}",(Long) request.getSession().getAttribute(Contants.SESSIONLOGIN));
            log.info("当前线程id={}",Thread.currentThread().getId());
            return true;

        } else if (request.getSession().getAttribute(Contants.SESSION_USERID) != null) {
            log.info("用户已经登入,id={}", request.getSession().getAttribute(Contants.SESSION_USERID));

            //进入拦截器后,给threadLocal绑定session,让后面需要的公共字段自动填充的时候,填充这个updateUser
            //每次http请求,会分配一个新的线程来处理
            BaseContext.setThreadLocal((Long) request.getSession().getAttribute(Contants.SESSION_USERID));
            log.info("拦截器这里设置了ThreadLocal,值为:{}",(Long) request.getSession().getAttribute(Contants.SESSION_USERID));
            log.info("当前线程id={}",Thread.currentThread().getId());
            return true;
        }
        //这里应该跳转到登入页面,如何做?
        //5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
        log.info("用户未登入,通过输出流方式向客户端页面响应数据,打回登入页面");
        response.getWriter().write(JSON.toJSONString(RetObj.error("NOTLOGIN")));//与前端request.js中的代码呼应
        return false;
    }

package cn.edu.uestc.ruijitakeout.common.config;
@Slf4j
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 拓展消息转换器
     * @param converters
     */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("拓展消息转换器成功加载");
        //创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用Jackson将Java对象转为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的消息转换器对象追加到mvc框架的转换器集合中
        converters.add(0,messageConverter);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //重写方法,添加拦截器方法
        registry.addInterceptor(loginInterceptor())
                //拦截哪些路径
                .addPathPatterns("/**")
                //不拦截路径
                .excludePathPatterns("/employee/backend/page/login/login.do",
                        //"/backend/**",
                        "/employee/backend/page/login/logout.do",
                        //"/front/**",
                        "/error",
                        "/user/**"


                );
    }

    @Bean
    public LoginInterceptor loginInterceptor(){
        return new LoginInterceptor();
    }

}

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

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

相关文章

体验 Dolly V2

体验 Dolly V2 1. 什么是 Dolly V22. Github 地址3. 安装 Miniconda34. 创建虚拟环境5. 部署 Dolly V26. 编写测试程序7. 运行测试程序 1. 什么是 Dolly V2 Databricks的dolly-v2-12b&#xff0c;是一个在 Databricks 机器学习平台上训练的指令跟随型大型语言模型&#xff0c;…

【2023/05/06】EDSAC

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第1天。 Share 去成为你本该成为的人&#xff0c;任何时候都不会太晚。 &#xff08;出处&#xff1a;乔治艾略特&#xff09; Day1 EDSAC&#xff1a;存储程序式计算机的开山之作。 part1 EDSAC&…

redis(3)

redis客户端登陆: redis-cli -h host -p port -a password String类型的应用场景: 1)String通常用于保存单个字符串或者JSON格式的字符串数据 2)因为String类型通常是二进制安全的&#xff0c;因此你完全可以把一个图片内容当作字符串来进行存储 3)计数器&#xff0c;是常规的k…

java错题总结(31-33页)

假定Base b new Derived&#xff08;&#xff09;; 调用执行_360笔试题_牛客网 ABDC 只要是被子类重写的方法&#xff0c;不被super调用都是调用子类方法 ------------------------------------------------------------------------------------------- --------…

【Linux】Linux安装Redis(图文解说详细版)

文章目录 前言第一步&#xff0c;下载安装包第二步&#xff0c;上传安装包到/opt下&#xff08;老规矩了&#xff0c;安装包在opt下&#xff09;第三步&#xff0c;解压安装包第四步&#xff0c;编译第五步&#xff0c;安装第六步&#xff0c;配置redis第七步&#xff0c;设置开…

Matplotlib 初使用

五一假期时候&#xff0c;我在自己的电脑上安装了 matplotlib 安装步骤请转至&#xff1a;matplotlib 安装介绍 现在&#xff0c;我终于可以在自己电脑上学习 matolotlib 数学绘图库了&#xff0c; 说终于两个字&#xff0c;是完全是在安装过程中确实历经了折磨&#xff0c;根…

基于AT89C51单片机的电子钟设计与仿真

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87759735?spm1001.2014.3001.5503 源码获取 主要内容&#xff1a; 设计一个电子钟,实现对时、分、秒的显示的控制,电路采用24小时计时方式。另一个功能是秒表功能…

mysql : name like “%name“; 索引一定失效吗?

场景如下&#xff1a; MySQL版本如下&#xff1a; 表结构如下&#xff1a;索引结构如下&#xff1a; 查询语句以及执行计划如下&#xff1a; 第一条查询语句&#xff1a; select name from em where name like ‘%b’; 可以发现使用了name 字段创建的索引 第二条查询语句&…

02-Java基础编程

Java基础编程 Java 基础语法Java 标识符变量变量的类型Java 基本数据类型基本数据类型转换 运算符常见运算符运算符的优先级 程序流程控制分支语句循环结构常用的循环结构循环的嵌套break 和 continue 关键字 数组一维数组多维数组的使用Arrays 工具类的使用数组中常见的异常 J…

AcWing——方格迷宫(有点不一样的迷宫问题)

4943. 方格迷宫 - AcWing题库 1、题目 给定一个 n 行 m 列的方格矩阵。 行从上到下依次编号为 1∼n&#xff0c;列从左到右依次编号为 1∼m。 第 i 行第 j 列的方格表示为 (i,j)。 矩阵中的方格要么是空地&#xff08;用 . 表示&#xff09;&#xff0c;要么是陷阱&#xf…

前后端身份认证:session身份认证,JWT认证

文章目录 前后端身份认证1、概述2、不同开发模式下的身份认证3、关于HTTP协议的无状态性4、Cookie4.1 介绍4.2 cookie特点4.3 cookie分类4.4 Cookie在session身份认证中的作用4.5 Cookie不具有安全性 5、Session认证5.1 Session认证过程5.2 Session认证原理图5.3 Session认证的…

【Linux】多线程 --- 线程概念 控制 封装

从前种种&#xff0c;譬如昨日死。从后种种&#xff0c;往如今日生。 文章目录 一、线程概念1.重新理解用户级页表1.1 进程资源如何进行分配呢&#xff1f;&#xff08;地址空间页表&#xff09;1.2 虚拟地址如何转换到物理地址&#xff1f;&#xff08;页目录页表项&#xff0…

11 KVM虚拟机配置-配置虚拟设备(存储)

11 KVM虚拟机配置-配置虚拟设备(存储) 文章目录 11 KVM虚拟机配置-配置虚拟设备(存储)11.1 存储设备概述11.2 存储设备元素介绍11.3 存储设备配置示例 11.1 存储设备概述 虚拟机XML配置文件使用devices元素配置虚拟设备&#xff0c;包括存储设备、网络设备、总线、鼠标等。 X…

【LeetCode】494. 目标和

494. 目标和 思路 首先&#xff0c;将这道题想成 0-1背包问题&#xff0c;我们最终要输出的结果是最多的方法数&#xff0c;因此 dp 数组需要记录具体的方法数。 状态定义 按照 0-1 背包问题的套路&#xff0c;我们将状态定义为 &#xff1a;dp[i][j] &#xff0c;表示「前 …

YOLOv5测距+碰撞检测

YOLOv5测距碰撞检测 1. 相关配置2. 测距原理3. 标定和测距4. 碰撞检测4.1 相关代码4.2 主代码 5. 实验效果 相关链接 1. YOLOV5 单目测距&#xff08;python&#xff09; 2. YOLOV7 单目测距&#xff08;python&#xff09; 3. 具体实现效果已在Bilibili发布&#xff0c;点击…

string的介绍

string是c中表示字符串的字符串类&#xff0c;要使用需要包头文件&#xff1a;#include<string> 先了解一下string的一些信息 string看起来是一个类&#xff0c;但实际上是typedef的模板。 在cplusplus.com网站上&#xff0c;string的相关信息 模板的实例化结果有以下几个…

【unity小创意】相机的正反操作实现场景的二维跳跃

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

Spring的第十二阶段(01):Spring实现AOP的简单使用

1、使用Spring实现AOP简单切面编程 需要导入工程的jar包 Spring的核心包 spring-beans-4.0.0.RELEASE.jar spring-context-4.0.0.RELEASE.jar spring-core-4.0.0.RELEASE.jar spring-expression-4.0.0.RELEASE.jarSpring的测试包 spring-test-4.0.0.RELEASE.jarSpring日记相…

【网络安全】这套面试题,让你提前预判面试官的预判!

最近这个帖子的点赞和收藏变高起来了&#xff0c;许多小伙伴在问我安全大厂的面试题没有&#xff0c;我准备利用一些时间把这套面试宝典整理一下&#xff01; 今天有同学拿着他准备的面试问题清单给我看&#xff0c;看还有没有遗漏的&#xff0c;我看了下&#xff0c;觉得还是…

Spring-Bean管理-注解

组件注册 Component/Controller/Service/Repostory :注册自定义组件到容器中 加上约定的注解。 在Configuration注解的类中配置包扫描器 ComponentScan(vlaue "cn.shaoxiongdu") Configuration: 标注配置类 Scope &#xff1a; 配置是否为单实例 prototype: 多实…