验证码登录开发----手机验证码登录

news2025/1/11 20:39:54

手机验证码登录

需求分析

为了方便用户登录,移动端通常都会提供通过手机验证码登录的功能

手机验证码登录的优点:

  • 方便快捷、无需注册,直接登录
  • 使用短信验证码作为登录凭证,无需记忆密码
  • 安全

登录流程:

输入手机号>获取验证码>输入验证码>点击登录>登录成功

注意:通过手机验证码登录,手机号是区分不同用户的标识

代码开发–梳理交互过程

在开发代码之前,需要梳理一下登录时前端页面和服务端的交互过程:

  1. 在登录页面(front/page/login.html)输入手机号,点击【获取验证码】按钮,页面发送ajax请求,在服务端调用短信服务API给指定手机号发送验证码短信
  2. 在登录页面输入验证码,点击【登录】按钮,发送ajax请求,在服务端处理登录请求

开发手机验证码登录功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可

代码开发–准备工作

在开发业务功能前,先将需要用到的类和接口基本结构创建好

  • 实体类User

    /**
     * 用户信息
     */
    @Data
    public class User implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        private Long id;
    
    
        //姓名
        private String name;
    
    
        //手机号
        private String phone;
    
    
        //性别 0 女 1 男
        private String sex;
    
    
        //身份证号
        private String idNumber;
    
    
        //头像
        private String avatar;
    
    
        //状态 0:禁用,1:正常
        private Integer status;
    }
    
    
  • Mapper接口UserMapper

  • 业务层接口UserService

  • 业务层实现类UserServiceImpl

  • 控制层UserController

    @Slf4j
    @RestController
    @RequestMapping("/user")
    public class UserController {
    
        @Autowired
        private UserService userService;
    
        /*
        * 发送手机短信验证码
        * */
        @PostMapping("/sendMsg")
        public R<String> sendMsg(@RequestBody User user, HttpSession session){
            //获取手机号
            String phone = user.getPhone();
            if(StringUtils.isNotEmpty(phone)){
                //生成随机的4位验证码
                String validateCode = ValidateCodeUtils.generateValidateCode(4).toString();
                log.info("ValidateCode:{}",validateCode);
                //调用阿里云提供的短信服务API完成发送短信
                //SMSUtils.sendMessage("申请签名","模板code",phone,validateCode);
                //需要将生成的验证码保存到Session
                session.setAttribute(phone,validateCode);
                return R.success("手机验证短信发送成功!!!");
            }
            return R.error("短信发送失败!!!");
        }
    
        /*
         * 移动端用户登录
         * */
        @PostMapping("/login")
        public R<User> login(@RequestBody Map map, HttpSession session){
            /*这里会出现一个问题前端传过来的验证码User类不能接收
            * 有两种解决方法:
            * 1.我们可以创建一个UserDto,继承User
            * 2.还有一种就是可以使用Map来接收,用键值对的接收
            * */
            log.info(map.toString());
            //首先获取手机号
            String phone = map.get("phone").toString();
            //其次获取验证码
            String code = map.get("code").toString();
            //从session中获得保存到的验证码
            Object codeSession = session.getAttribute(phone);
            //进行验证码的比对(页面提交的验证码和Session中保存的验证码比对)
            if (codeSession!=null&&codeSession.equals(code)){
                //如果能够比对成功,说明登录成功
    
                LambdaQueryWrapper<User>queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(User::getPhone,phone);
                User user = userService.getOne(queryWrapper);//手机是唯一标识
                if (user==null){
                    //判断当前手机号对应的用户是否为新用户,如果是新用户就自动完成注册
                    user = new User();
                    user.setPhone(phone);
                    user.setStatus(1);//设置状态 1表示正常
                    userService.save(user);
                }
                session.setAttribute("user",user.getId());//经过过滤器会去校验,所以我们需要将用户id存储到session中去
    
                return R.success(user);
            }
            return R.error("登录失败!!!");
        }
    }
    
  • 工具类SMSUtils、ValidateCodeUtils

    • SMSUtils

      /**
       * 短信发送工具类
       */
      public class SMSUtils {
      
      	/**
      	 * 发送短信
      	 * @param signName 签名
      	 * @param templateCode 模板
      	 * @param phoneNumbers 手机号
      	 * @param param 参数
      	 */
      	public static void sendMessage(String signName, String templateCode,String phoneNumbers,String param){
      		DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "", "");
      		IAcsClient client = new DefaultAcsClient(profile);
      
      		SendSmsRequest request = new SendSmsRequest();
      		request.setSysRegionId("cn-hangzhou");
      		request.setPhoneNumbers(phoneNumbers);
      		request.setSignName(signName);
      		request.setTemplateCode(templateCode);
      		request.setTemplateParam("{\"code\":\""+param+"\"}");
      		try {
      			SendSmsResponse response = client.getAcsResponse(request);
      			System.out.println("短信发送成功");
      		}catch (ClientException e) {
      			e.printStackTrace();
      		}
      	}
      
      }
      
      
    • ValidateCodeUtils

    /**
     * 随机生成验证码工具类
     */
    public class ValidateCodeUtils {
        /**
         * 随机生成验证码
         * @param length 长度为4位或者6位
         * @return
         */
        public static Integer generateValidateCode(int length){
            Integer code =null;
            if(length == 4){
                code = new Random().nextInt(9999);//生成随机数,最大为9999
                if(code < 1000){
                    code = code + 1000;//保证随机数为4位数字
                }
            }else if(length == 6){
                code = new Random().nextInt(999999);//生成随机数,最大为999999
                if(code < 100000){
                    code = code + 100000;//保证随机数为6位数字
                }
            }else{
                throw new RuntimeException("只能生成4位或6位数字验证码");
            }
            return code;
        }
    
        /**
         * 随机生成指定长度字符串验证码
         * @param length 长度
         * @return
         */
        public static String generateValidateCode4String(int length){
            Random rdm = new Random();
            String hash1 = Integer.toHexString(rdm.nextInt());
            String capstr = hash1.substring(0, length);
            return capstr;
        }
    }

代码开发–修改LoginCheckFilter

前面我们已经完成了LoginCheckFilter过滤器的开发,此过滤器用于检查用户的登录状态。我们在进行手机验证码登录时,发送的请求需要在此过滤器处理时直接放行

LoginCheckFilter过滤器的编写链接

@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")//利用过滤器拦截,拦截所有的请求  urlPatterns = "/*"
@Slf4j
public class LoginCheckFilter implements Filter {
    //路径匹配器,并支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;


        //1. 获取本次请求的URI
        String requestURI = request.getRequestURI();
        //如果请求路径为"/backend/index.html"会与放行的"/backend/**"路径匹配不上,会导致无法识别,这时需要路径匹配器
        log.info("拦截到的请求:{}",requestURI);

        //1.2 定义不需要处理的请求路径,比如:登录页面,退出,静态资源,主要是拦截controller资源
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/common/**",
                "/user/sendMsg",//移动端发送短信
                "/user/login"//移动端登录
        };
        //2. 判断本次请求是否需要处理,封装一个方法类进行比较
        boolean check = check(urls, requestURI);
        //3. 如果不需要处理,则直接放行
        if (check) {
            log.info("本次请求{}不需要处理",requestURI);
            filterChain.doFilter(request,response);//放行
            return;
        }
        //   4-1. 判断登录状态,如果已登录,则直接放行  后台系统
        if(request.getSession().getAttribute("employee")!=null){
            log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));

            Long employeeId =(Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(employeeId);//保存用户id,用于公共字段自动填充使用

            long id = Thread.currentThread().getId();
            log.info("线程id为:{}",id);
            //已经登录,直接放行
            filterChain.doFilter(request,response);//放行
            return;
        }

        //   4-2. 判断登录状态,如果已登录,则直接放行  移动端判断
        if(request.getSession().getAttribute("user")!=null){
            log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("user"));

            Long userId =(Long) request.getSession().getAttribute("user");
            BaseContext.setCurrentId(userId);//保存用户id,用于公共字段自动填充使用

            long id = Thread.currentThread().getId();
            log.info("线程id为:{}",id);
            //已经登录,直接放行
            filterChain.doFilter(request,response);//放行
            return;
        }
        //5. 如果未登录则返回未登录结果,通过输出流的方式向客户端响应数据
        log.info("用户未登录");
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));

        return;
    }
    /*
    * 路径匹配,检查本次请求是否需要放行
    * */
    public boolean check(String[]urls,String requestURI){
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match) {
                return true;
            }
        }
        return false;
    }
}

在这里插入图片描述

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

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

相关文章

06-文章搜索页面

文章搜索页面 6-1&#xff1a;开篇 再上一章中&#xff0c;我们完成了 热搜首页 的开发&#xff0c;虽然经历了 ”千辛万苦“ &#xff0c;但是对大家来说&#xff0c;应该也是收获满满。 那么在这一章节&#xff0c;我们将会进入新的篇章&#xff0c;来到 文章搜索 页面的开…

【C++】深度剖析string类的底层结构及其模拟实现

文章目录 前言1. string的结构2. 构造、析构2.1 无参构造2.2 带参构造2.3 问题发现及修改c_stroperator []析构 2.4 合二为一 ——全缺省 3. 拷贝构造3.1 浅拷贝的默认拷贝构造3.2 深拷贝拷贝构造的实现 4. 赋值重载4.1 浅拷贝的默认赋值重载4.2 深拷贝赋值重载的实现 5. strin…

ES6 中的类(class)

前言 ES6 引入的 class 关键字具有定义类的能力。类是 ECMAScript 中新的基础性语法糖。虽然 ES6 表面上看起来可以支持正式的面向对象编程&#xff0c;但实际上它背后使用的依旧是原型和构造函数的概念。&#xff08;笔记内容参考《JavaScript 高级程序设计&#xff08;第 4 …

TTL反相器、OC门、TS门、推挽输出、开漏输出

TTL反相器 这是一个TTL反相器&#xff0c;这是经过了很多工程师多种设计最终沉淀出来的电路&#xff0c;这个电路是比较成熟的。我们只需要对这个电路进行解析即可&#xff0c;不需要再去研究当初是如何设计出来的。 学过CMOS应该知道&#xff0c;右侧的输出级其实也是个推挽输…

为什么程序实际可用内存会远超物理内存?

背景介绍 不知道在你刚接触计算机的时候&#xff0c;有没有这么一个疑问&#xff1a;“为什么我的机器上只有两个G 的物理内存&#xff0c;但我却可以使用比这大得多的内存&#xff0c;比如 256T&#xff1f;”反正我当时还是挺疑惑的&#xff0c;不过现在我可以来告诉你这个答…

如何将销售效果最大化:从人工智能聊天机器人到即时聊天

聊天机器人的崛起 从几年前开始&#xff0c;客户支持是聊天机器人使用的第一个爆发点。B2C引领潮流&#xff0c;B2B紧随其后。市场营销和销售最近也开始加入&#xff0c;让聊天机器人承担起迎接和引导网站游客的任务。现在&#xff0c;人工智能已经进入聊天&#xff0c;可以说…

机器学习 第一周

目录 1. 什么是机器学习(课本给出的部分定义) 我理解的机器学习:

Java基础:IO流有哪些,各有什么特点和功能

具体操作分成面向字节(Byte)和面向字符(Character)两种方式。 如下图所示&#xff1a; IO流的三种分类方式 IO流的层次结构 IO流的常用基类有&#xff1a; 字节流的抽象基类&#xff1a;InputStream和OutputStream&#xff1b; 字符流的抽象基类&#xff1a;Reader和Writer…

20、单元测试

文章目录 1、JUnit5 的变化2、JUnit5常用注解3、断言&#xff08;assertions&#xff09;1、简单断言2、数组断言3、组合断言4、异常断言5、超时断言6、快速失败 4、前置条件&#xff08;assumptions&#xff09;5、嵌套测试6、参数化测试7、迁移指南 【尚硅谷】SpringBoot2零基…

JAVA性能优化实例

目录 概述 Sql性能优化 多线程 利用内存缓存 功能优化 参考博客 概述 性能优化的几个点&#xff0c;大致可以分为&#xff1a; sql优化使用多线程利用内存&#xff0c;缓存等&#xff0c;将固定不常更改的数据放入在&#xff0c;存取更快的内存当中功能实现逻辑优化 Sql性…

五分钟,说说Python 中多线程共享全局变量的问题

嗨害大家好鸭&#xff01;我是爱摸鱼的芝士❤ 写在前面不得不看的一些P话&#xff1a; Python 中多个线程之间是可以共享全局变量的数据的。 但是&#xff0c;多线程共享全局变量是会出问题的。 假设两个线程 t1 和 t2 都要对全局变量g_num (默认是0)进行加1运算&#xff0c…

日常项目技术方案脉络

开篇引砖 软件在其生命周期中&#xff0c;当其进入稳定期后&#xff0c;大部分时间都处于迭代更新维护阶段。在这漫长的三年甚至五年的存活期内&#xff0c;我们需要面对林林种种大大小小的需求。今天我们就聊聊在这段期间&#xff0c;如何快速产出一份合格的技术方案。 方案给…

JavaScript经典教程(一)-- HTML基础部分

179&#xff1a;HTML基础部分&#xff08;元素分类、特性、特殊元素等&#xff09; 1、复习&#xff1a; HTML&#xff1a; 超文本标记语言 CSS&#xff1a; 层叠样式表 JavaScript&#xff1a; 脚本语言 http&#xff1a;超文本传输协议 https&#xff1a; 经过ssl加密的超…

如何搭建关键字驱动自动化测试框架?这绝对是全网天花板的教程

目录 1. 关键字驱动自动化测试介绍 2. 搭建关键字驱动自动化测试框架 步骤1&#xff1a;选择测试工具 步骤2&#xff1a;定义测试用例 步骤3&#xff1a;编写测试驱动引擎 步骤4&#xff1a;实现测试关键字库 步骤5&#xff1a;执行测试 3. 实现关键字驱动自动化测试的关…

c/c++:gets(),fgets(),puts(),fputs(),strlen(),字符串拼接函数

c/c&#xff1a;gets()&#xff0c;fgets()&#xff0c;puts()&#xff0c;fputs()&#xff0c;strlen()&#xff0c;字符串拼接函数 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;此时学会c的话&#xff0c; 我所知道的周…

C语言程序环境与预处理回顾总结

大概讲解与铺垫 首先&#xff0c;什么叫c语言的源代码&#xff1f;也就是我自己写的.c文件里面的代码&#xff0c;这个就叫做源代码。然后需要知道的是计算机他只认识二进制&#xff0c;因此他只能接收与执行二进制指令。也就是可执行的机器指令。然后我们必须得知道&#xff…

JavaWeb——tomcat(安装使用)

目录 WEB服务器-Tomcat 服务器概述 Web服务器 Tomcat Tomcat下载 安装与卸载 启动与关闭 WEB服务器-Tomcat 服务器概述 服务器硬件&#xff1a; 指的也是计算机&#xff0c;只不过服务器要比我们日常使用的计算机大很多。 服务器&#xff0c;也称伺服器。是提供计算服务…

【数据结构】第十一站:链式二叉树

目录 一、二叉树的创建 二、二叉树的遍历 1.前序中序后序遍历 2.层序遍历 三、二叉树的节点个数以及高度等 四、二叉树的构建和销毁 五、DFS和BFS 一、二叉树的创建 为了方便后面的讨论&#xff0c;我们在这里先手撕一颗二叉树 typedef int BTDateType; typedef struct …

Qt Designer

Qt Designer——即Qt设计师&#xff0c;是QT项目开发的可视化图形界面编辑器&#xff0c;通过设计师可以很方便地创建图像界面文件*.ui&#xff0c;然后将ui文件应用的源代码中&#xff0c;做到所见即所得&#xff0c;让界面的设计变得十分简单。下面介绍Qt Designer的简单使用…

使用PowerShell自动部署ASP.NetCore程序到IIS

asp.net core 安装asp.net core sdk https://dotnet.microsoft.com/en-us/download/dotnet/3.1 创建asp.net core项目 dotnet new webapi运行项目 访问https://localhost:5001/WeatherForecast iis配置 安装iis 以管理员身份运行powershell Enable-WindowsOptiona…