Java简单实现短信验证登录(Session、Redis)

news2024/11/16 1:23:22

前端设计

<div class="login-form">
        <div style="display: flex; justify-content: space-between">
          <el-input style="width: 60%" placeholder="请输入手机号" v-model="form.phone" >
          </el-input>
          <el-button style="width: 38%" @click="sendCode" type="success" :disabled="disabled">{{codeBtnMsg}}</el-button>
        </div>

        <div style="height: 5px"></div>
        <el-input placeholder="请输入验证码" v-model="form.code">
        </el-input>
        <div style="text-align: center; color: #8c939d;margin: 5px 0">未注册的手机号码验证后自动创建账户</div>
        <el-button @click="login" style="width: 100%; background-color:#f63; color: #fff;">登录</el-button>
        <div style="text-align: right; color:#333333; margin: 5px 0"><a href="/login2.html">密码登录</a></div>
</div>



<script>

  const app = new Vue({
    el: "#app",
    data: {
      radio: "",
      disabled: false, // 发送短信按钮
      codeBtnMsg: "发送验证码", // 发送短信按钮提示
      form:{
      }
    },
    methods: {
      login(){
        if(!this.radio){
          this.$message.error("请先确认阅读用户协议!");
          return
        }
        if(!this.form.phone || !this.form.code){
          this.$message.error("手机号和验证码不能为空!");
          return
        }
        axios.post("/user/login", this.form)
        .then(({data}) => {
            if(data){
              // 保存用户信息到session
              sessionStorage.setItem("token", data);
            }
            // 跳转到首页
            location.href = "/index.html"
        })
        .catch(err => this.$message.error(err))
      },
      goBack(){
        history.back();
      },
      sendCode(){
        if (!this.form.phone) {
          this.$message.error("手机号不能为空");
          return;
        }
        // 发送验证码
        axios.post("/user/code?phone="+this.form.phone)
          .then(() => {})
          .catch(err => {
            console.log(err);
            this.$message.error(err)
          });
        // 禁用按钮
        this.disabled = true;
        // 按钮倒计时
        let i = 60;
        this.codeBtnMsg = (i--) + '秒后可重发'
        let taskId = setInterval(() => this.codeBtnMsg = (i--) + '秒后可重发', 1000);
        setTimeout(() => {
          this.disabled = false;
          clearInterval(taskId);
          this.codeBtnMsg = "发送验证码";
        }, 59000)
      }
    }
  })
</script>

在这里插入图片描述

一、基于Session实现登录

流程

在这里插入图片描述
发送验证码:

用户在提交手机号后,会校验手机号是否合法,如果不合法,则要求用户重新输入手机号

如果手机号合法,后台此时生成对应的验证码,同时将验证码进行保存,然后再通过短信的方式将验证码发送给用户

短信验证码登录、注册:

用户将验证码和手机号进行输入,后台从session中拿到当前验证码,然后和用户输入的验证码进行校验,如果不一致,则无法通过校验,如果一致,则后台根据手机号查询用户,如果用户不存在,则为用户创建账号信息,保存到数据库,无论是否存在,都会将用户信息保存到session中,方便后续获得当前登录信息

校验登录状态:

用户在请求时候,会从cookie中携带者JsessionId到后台,后台通过JsessionId从session中拿到用户信息,如果没有session信息,则进行拦截,如果有session信息,则将用户信息保存到threadLocal中,并且放行。

具体实现

正则工具类: pringBoot后端一些常见的正则表达校验
Result

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    private Boolean success;
    private String errorMsg;
    private Object data;
    private Long total;

    public static Result ok(){
        return new Result(true, null, null, null);
    }
    public static Result ok(Object data){
        return new Result(true, null, data, null);
    }
    public static Result ok(List<?> data, Long total){
        return new Result(true, null, data, total);
    }
    public static Result fail(String errorMsg){
        return new Result(false, errorMsg, null, null);
    }
}
  • 发送验证码
 @Override
    public Result sendCode(String phone, HttpSession session) {
        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 3.符合,生成验证码
        String code = RandomUtil.randomNumbers(6);

        // 4.保存验证码到 session
        session.setAttribute("code",code);
        // 5.发送验证码 (下面介绍阿里云SMS服务发送)
        log.debug("发送短信验证码成功,验证码:{}", code);
        // 返回ok
        return Result.ok();
    }
  • 登录
@Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        // 1.校验手机号
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 3.校验验证码
        Object cacheCode = session.getAttribute("code");
        String code = loginForm.getCode();
        if(cacheCode == null || !cacheCode.toString().equals(code)){
             //3.不一致,报错
            return Result.fail("验证码错误");
        }
        //一致,根据手机号查询用户
        User user = query().eq("phone", phone).one();

        //5.判断用户是否存在
        if(user == null){
            //不存在,则创建
            user =  createUserWithPhone(phone);
        }
        //7.保存用户信息到session中
        session.setAttribute("user",user);

        return Result.ok();
    }

二、基于Redis实现短信登录

流程

在这里插入图片描述
当注册完成后,用户去登录会去校验用户提交的手机号和验证码,是否一致,如果一致,则根据手机号查询用户信息,不存在则新建,最后将用户数据保存到redis,并且生成token作为redis的key,当我们校验用户是否登录时,会去携带着token进行访问,从redis中取出token对应的value,判断是否存在这个数据,如果没有则拦截,如果存在则将其保存到threadLocal中,并且放行。

实现

自定义常量

public class RedisConstants {
    public static final String LOGIN_CODE_KEY = "login:code:";
    public static final Long LOGIN_CODE_TTL = 5L;
    public static final String LOGIN_USER_KEY = "login:token:";
    public static final Long LOGIN_USER_TTL = 360000L;
}
  • 发送验证码
@Resource
 private StringRedisTemplate stringRedisTemplate;



 @Override
    public Result sendCode(String phone, HttpSession session) {
        //1.校验手机号
        if (!RegexUtils.isPhoneInvalid(phone)) {
            //2.不符合
            return Result.fail("手机号格式错误!");
        }


        //3.符合
        String code = RandomUtil.randomNumbers(6);


        //4.保存验证码到 redis
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);

        //5.发送验证码
        log.debug("发送短信验证码成功,验证码: " + code);
       
        //返回ok
        return Result.ok();
    }
  • 登录
 @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        // 1.校验手机号
        String phone = loginForm.getPhone();

        if (!RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机格式错误");
        }
     
        //2.从 redis 获取,检验验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();

        if (cacheCode == null || !cacheCode.toString().equals(code)) {
            //3.不一致,报错
            return Result.fail("验证码错误");
        }
        //4.一致,根据手机号查询用户
        User user = query().eq("phone", phone).one();
        //5.判断用户是否存在
        if (user == null) {
            //6.不存在,创建用户并保存
            user = createUserWithPhone(phone);
        }


       

        // 7.保存用户信息到 redis
        // 7.1 随机生成token,作为登录令牌
        String token = UUID.randomUUID().toString(true);


        // 7.2 将User对象装维HashMap存储
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString())); //idInterger类型转为String才能存

        // 7.3 存储
        String tokenKey = LOGIN_USER_KEY + token;
        stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
        stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);


        // 8. 返回token
        return Result.ok(token);
    }

三、阿里云Sms服务发送短信

参考文章:阿里云——Java实现手机短信验证码功能

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

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

相关文章

winform的RichTextBox控件追加文本及图片(实现笔记录入和笔记搜索功能)

在工作中&#xff0c;在工作中&#xff0c;难免有一些笔记要记录下来&#xff0c;方便后续工作中快速找到。之前用的是共享文档来记录的&#xff0c;但有一个缺点就是随着写的内容越来越多&#xff0c;打开变得很慢&#xff0c;搜索更加慢&#xff0c;网络不好的时候&#xff0…

Redis的常用数据结构之有序集合类型

有序集合的特点 集合中的元素有序&#xff0c;不可以重复与列表通过索引实现有序不同&#xff0c;有序集合实现有序的方式是通过给每一个集合元素设置一个分数score字段作为排序依据集合中的元素不能重复&#xff0c;但是score可以重复无法通过某一个下标的方式获取元素单个集…

大数据:spark环境搭建,local模式,standalone模式,zookeeper standby,yarn模式

大数据&#xff1a;spark环境搭建&#xff0c;local模式 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;or…

chatgpt赋能python:Python列表倒序排列

Python列表倒序排列 介绍 Python是一种强大的编程语言&#xff0c;经过多年的发展&#xff0c;它已成为最受欢迎的编程语言之一。Python列表是一个非常有用的数据结构&#xff0c;它允许您将多个项目组合在一起&#xff0c;并可以轻松地对它们进行排序和操作。本篇文章将介绍…

Cubase12没有声音解决办法(Windows 11专用)

本文章由CSDN 不想加班呀 原创&#xff0c;转载请注明出处。 作者首页&#xff1a;不想加班呀的博客_CSDN博客-Python爬虫,电脑小知识,程序员剪视频领域博主 目录 前言 解决办法 第一步&#xff08;进入系统硬件和声音设置界面&#xff09; 第二步&#xff08;在声音设置中…

【正点原子STM32连载】 第二十五章 TFT-LCD(MCU屏)实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第二十…

05.JavaWeb-Servlet

1.Servlet基础 1.1 Servlet概述 用于处理客户端传来的HTTP请求&#xff0c;并返回一个响应&#xff08;特点&#xff1a;方便、跨平台、灵活性和可扩展性&#xff09; 1.2 Servlet开发入门 1.2.1 Servlet接口及实现类 1.最基本的接口是javax.servlet.Servlet&#xff0c;Ser…

高完整性系统工程(十二):Separation Logic for Automated Verification

目录 1. INTRODUCTION TO SEPARATION LOGIC 分离逻辑 1.1 霍尔推理&#xff08;Hoare Reasoning&#xff09; 1.2 堆指针的影响 1.3 全局和局部推理&#xff08;Global and Local Reasoning&#xff09; 1.4 组合推理&#xff08;Compositional Reasoning&#xff09; 1.…

SpringBoot通过加装外部JAR包中的类实现业务插件功能

综合记录一下关于ClassLoader和Spring Bean的动态加载卸载功能 目录 一、需要说明二、总体设计三、具体设计3.1 加载卸载Bean工具类3.2 创建卸载方法3.3 创建加载方法3.4 创建获取具体服务类方法 四、总结 一、需要说明 有一个公共的发送通知的接口&#xff0c;这个接口需要做…

CUDA配置正确,但是torch.cuda.is_available()却是False的解决方案

1.torch.cuda.is_available()返回为False 有时候我们想要使用GPU加速&#xff0c;但是发现CUDA、pytorch都安装好了&#xff0c;且版本也匹配&#xff0c;但是仍然无法使用GPU,显示信息如下&#xff1a; 这时候我们可以看看是不是我们的Pytorch的问题 2.输入下面命令查看pyto…

(学习日记)2023.04.26

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

cesium-native编译

我相信点进这个博客的都是一些cesium专业人才&#xff0c;这文章只起了一个抛砖引玉的作用&#xff0c;希望各位人才不惜赐教。 Github地址&#xff1a;CesiumGS/cesium-native (github.com) 编译需求&#xff1a;升级公司的3dtile的架构&#xff0c;提高性能 博客目的&…

(转载)基于混合粒子群算法的TSP问题求解(matlab实现)

1 理论基础 标准粒子群算法通过追随个体极值和群体极值完成极值寻优&#xff0c;虽然操作简单&#xff0c;且能够快速收敛&#xff0c;但是随着迭代次数的不断增加&#xff0c;在种群收敛集中的同时&#xff0c;各粒子也越来越相似&#xff0c;可能在局部最优解周边无法跳出。…

福利·分析

竞争使得生产者剩余和消费者剩余的和最大化 无谓损失指的是由于过量生产或生产不足造成的消费者剩余和生产者剩余的净损失。 税收与补贴的福利分析 从量税效果&#xff1a; 为简单期间&#xff0c;我们对某种商品征收从量税&#xff1a;对每一销售的单元&#xff0c;征收特定…

汽车电子设计之AUTOSAR中CanNM模块

目录 前言 正文 网络节点类型 仅本地唤醒 仅网络唤醒 本地网络唤醒 KL15电唤醒 NM状态机 Bus Sleep Mode Network Mode Prepare Bus-Sleep Mode Passive Mode 状态机时间参数总结 NM状态机切换 网络管理报文结构 NM报文总体结构解析 CBV详解 常用函数接口 前言…

matlab给变量名称

效果 做法&#xff1a; 构建table-> ‘VariableNames’,{‘y’,‘x’} adata; a(:,2)linspace(0.1,4.1,41); tbltable(a(:,1),a(:,2), VariableNames,{y,x});

Verilog学习(SPI协议的Flash驱动控制)

目录 一、SPI通信协议 1.1 SPI物理层 1.2 SPI协议层 二、实战 2.1 SPI控制FLASH实现全擦除代码编写 2.2 上板验证 一、SPI通信协议 1.1 SPI物理层 SPI通信模式为主-从模式 &#xff0c;分为一主一从、一主多从&#xff1a; 片选线CS用于主机选择对应的从机进行通信&…

html爱情表白神器,回忆纪念册(附源码)

文章目录 1.设计来源1.1 主界面1.2 相关界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/131022313 html爱情表白神器&#xff0c;回忆纪念册 html爱情表白神器&#x…

【JavaEE初阶】万字详解TCP/IP协议!!!(一)

文章目录 1. 应用层和传输层的联系2. UDP协议3. TCP协议3.1 TCP报头介绍3.2 TCP实现可靠传输的核心机制&#xff08;1&#xff09;确认应答&#xff08;2&#xff09;超时重传&#xff08;3&#xff09;连接管理建立连接&#xff08;三次握手&#xff09;断开连接(四次挥手) &a…

测试人挣破年入20万的束缚,从第一个python+selenium项目开始!

今天整理一下实战项目的代码共大家学习。&#xff08;注:项目是针对我们公司内部系统的测试&#xff0c;只能内部网络访问&#xff0c;外部网络无法访问&#xff09; 问&#xff1a; 1.外部网络无法访问&#xff0c;代码也无法运行&#xff0c;那还看这个项目有啥用 2.如何学…