手机验证码登录 -- 手把手教你做ssm+springboot入门后端项目黑马程序员瑞吉外卖(七)

news2024/11/26 22:30:51

文章目录

  • 前言
  • 一、短信发送
    • 1. 短信服务介绍
    • 2. 阿里云短信服务
    • 3. 代码开发
  • 二、手机验证码登录
    • 1. 需求分析
    • 2. 数据模型
    • 3. 代码开发
    • 4. 功能测试
  • 总结


前言

为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章中如果有记录错误,欢迎读者朋友们批评指正。
(博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)

一、短信发送

1. 短信服务介绍

目前市面上有很多第三方提供的短信服务,这些第三方短信服务会和各个运营商(移动、联通、电信》对接,我们只需要注册成为会员并且按照提供的开发文档进行调用就可以发送短信。需要说明的是,这些短信服务一般都是收费服务,常用短信服务如下:

  1. 阿里云
  2. 华为云
  3. 腾讯云
  4. 京东
  5. 梦网
  6. 乐信

2. 阿里云短信服务

1. 简介
阿里云短信服务(Short Message Service) 是广大企业客户快速触达手机用户所优选使用的通信能力。调用API或用群发助手,即可发送验证码、通知类和营销类短信,国内验证短信秒级触达,到达率最高可达99%;国际/港澳台短信覆盖200多个国家和地区,安全稳定,广受出海企业选用,应用场景如下

  1. 验证码
  2. 短信通知
  3. 推广短信

在这里插入图片描述

2. 注册并登录

在这里插入图片描述

3. 开通短信服务

在这里插入图片描述
4. 申请短信签名

在这里插入图片描述

5. 申请短信模板

在这里插入图片描述

短信模板包含短信发送内容、场景、变量信息,示例如下

在这里插入图片描述

6. 设置AccessKey

  1. 光标移动到用户头像上,在弹出的窗口中点击[AccessKey 管理] ,选开始使用子用户AccessKey(风险较小)
  2. 云账号 AccessKey 是您访问阿里云 API 的密钥,具有账户的完全权限,请务必妥善保管。不要以任何方式公开 AccessKey 到外部渠道(例如 Github),避免被他人利用造成 安全威胁。强烈建议您遵循 阿里云安全最佳实践,使用 RAM 用户(而不是云账号)的 AccessKey 进行 API 调用。

7. 创建用户

在这里插入图片描述

8. 授予新建用户短信服务的权限

在这里插入图片描述

9. 回收AccessKey

在这里插入图片描述

3. 代码开发

  1. 使用阿里云短信服务发送短信,可以参照官方提供的文档即可,具体开发步骤如下
  1. 导入maven坐标
 <!--阿里云短信服务-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.5.16</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
            <version>2.1.0</version>
        </dependency>
  1. 调用API(代码示例)

在这里插入图片描述

更多详情请访问【https://help.aliyun.com/document_detail/112148.html?spm=a2c4g.215758.0.0.47175d7azl2Ds9】

二、手机验证码登录

1. 需求分析

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

  1. 方便快捷,无需注册,直接登录
  2. 使用短信验证码作为登录凭证,无需记忆密码
  3. 安全
    3. 登录流程
    输入手机号>获取验证码>输入验证码>点击登录>登录成功

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

2. 数据模型

通过手机验证码登录时,涉及的表为user表,即用户表。结构如下

在这里插入图片描述

3. 代码开发

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

  1. 在登录页面(front/page/login.html)输入手机号,点击[获取验证码] 按钮,页面发送ajax请求,在服务端调用短信服务API给指定手机号发送验证码短信
  2. 在登录页面输入验证码,点击[登录]按钮,发送aiax请求,在服务端处理登录请求
  3. 开发手机验证码登录功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可

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
@Mapper
public interface UserMapper extends BaseMapper<User>{
}
  • 业务层接口 UserService
public interface UserService extends IService<User> {
}

  • 业务层实现类 UserServicelmpl
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService{
}
  • 控制层 UserController
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

}

  • 工具类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;
    }
}

3. 代码开发-修改LoginCheckFilter

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

在这里插入图片描述

在这里插入图片描述

4. 在LoginCheckFilter过滤器中扩展逻辑,判断移动端用户登录状态(4-2)

 @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

        log.info("拦截到请求:{}",requestURI);

        //定义不需要处理的请求路径
        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 empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(empId);

            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);

            filterChain.doFilter(request,response);
            return;
        }


        log.info("用户未登录");
        //5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;

    }        

5. 前端相关页面分析

在这里插入图片描述

6. 编写对应的controller方法

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 发送手机短信验证码
     * @param user
     * @return
     */
    @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("短信发送失败");
    }

    /**
     * 移动端用户登录
     * @param map
     * @param session
     * @return
     */
    @PostMapping("/login")
    public R<User> login(@RequestBody Map map, HttpSession session){
        log.info(map.toString());

        //获取手机号
        String phone = map.get("phone").toString();

        //获取验证码
        String code = map.get("code").toString();

        //从Session中获取保存的验证码
        Object codeInSession = session.getAttribute(phone);

        //进行验证码的比对(页面提交的验证码和Session中保存的验证码比对)
        if(codeInSession != null && codeInSession.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);
                userService.save(user);
            }
            session.setAttribute("user",user.getId());
            return R.success(user);
        }
        return R.error("登录失败");
    }

}

4. 功能测试

在这里插入图片描述

总结

欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下。
(博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)

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

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

相关文章

项目名称:网络聊天室

目录 一&#xff0c;简述 二&#xff0c;项目要求 三&#xff0c;程序流程图 服务器端&#xff1a; 客户端&#xff1a; 四&#xff0c;相关知识点 通信流程&#xff1a; 函数接口&#xff1a; 五&#xff0c;代码实现 客户端&#xff1a; 服务器&#xff1a; 主程序…

Android性能优化——启动优化

App 的启动速度是用户的第一体验&#xff0c;互联网中有一个八秒定律&#xff0c;如果用户等待八秒App 还没打开&#xff0c;70%的用户都会停止等待 一、启动分类 官方 App startup time 冷启动 耗时最多&#xff0c;衡量标准 热启动 最快。 后台&#xff5e;前台 温启动…

Git 新建本地仓库,推送到远程仓库

1、在项目的根目录右键 Git Bash Here 打开目录下的 git 命令 2、输入 git init 回车&#xff0c;初始化项目&#xff0c;把这个项目变成一个Git可以管理的仓库 项目根目录出现 .git 隐藏文件夹。这个目录是Git来跟踪管理版本库的&#xff0c;没事千万不要手动修改这个目录里面…

MateBook E Go Wi-Fi性能版(GK-W76)工厂模式win11原厂系统,含F10智能恢复功能

HUAWEI华为MateBook E平板笔记本电脑(GK-W76)原装出厂Windows11系统包&#xff0c;带F10一键智能还原 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、华为电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;zip 文件大小&#xff1a;1…

Linux基础(三)端口、进程及主机状态管理、环境变量、文件管理

目录 端口 nmap netstat 进程管理 查看进程 关闭进程 主机状态监控 系统资源top命令 磁盘信息监控 网络状态监控 环境变量 $符号 自己设置环境变量 自定义环境变量PATH Linux的文件和下载 压缩和解压 tar命令 zip和unzip命令 端口 每个电脑有一个ip地址&#xff…

自动化测试(一):网页结构分析与Google翻译2023.7.18爬虫实例

目录 1. 网页分析1.1 静态网页1.2 静态网页的爬取案例1.3 动态网页1.4 Google翻译2023.7.18爬虫实例1.4.1 基于网页分析的Google翻译2023.7.18爬虫实例1.4.2 基于Selenium的Google翻译2023.7.18爬虫实例 1. 网页分析 网页分析即通过检查元素&#xff0c;确定想提取的内容的区域…

pyright 的配置方法

珍惜发量,拥抱python&#x1f602; 可以很快的构建一个平台管理程序,嵌入式设备,芯片厂商&#xff0c;都是python 的示例代码了 Lua 真的很棒&#xff0c;值得嵌入式领域的推广 实时性要求不高的&#xff0c;嵌入式设备,可以考虑lua python PC 的程序 ,python/Rust/C &#xf…

波奇学Linux:冯诺依曼和进程

现代计算机大多是冯诺依曼体系结构&#xff0c;这是一种硬件结构&#xff0c;规定了设备组成和设备类型。 存储器:内存&#xff0c;磁盘/U盘是外存是输入输出设备。 输入设备&#xff1a;键盘/磁盘/摄像头/话筒/网卡 输出设备&#xff1a;显示器&#xff0c;播放器硬件&#…

Spring6 初始

Spring6 初始 文章目录 Spring6 初始每博一文案:1. 初始 Spring61.1 OCP开闭原则1.2 依赖倒置原则DIP1.3 控制反转IoC 2. Spring 初始2.1 Spring特点2.2 Spring6 的下载&#xff1a;2.3 Spring的jar文件 3. 第一个Spring 程序的编写4. 第一个Spring程序详细剖析4.1 bean标签的i…

SQL28 计算用户8月每天的练题数量

select day(date) as day,count(question_id) from question_practice_detail where month(date)8 and year(date)2021 group by date

C语言如何计算结构体大小(结构体的内存对齐)

前言&#xff1a; 结构体的内存对齐是有关结构体内容的很重要一个知识点&#xff0c;主要考察方式是计算结构体的字节大小。 引言&#xff1a; 当我们对计算结构体一无所知&#xff0c;我们不妨自己思索如何计算&#xff0c;是不是直接计算结构体成员变量占用内存的大小呢&a…

共享与协作:时下最热门的企业共享网盘推荐!

现代企业面临着越来越大的数据存储和共享压力。为了提高公司的生产力和效率&#xff0c;许多企业开始寻找共享网盘解决方案。这些共享网盘平台可以帮助企业集中管理文件和数据&#xff0c;并方便快速地与同事、客户或供应商共享。以下是几款好用的企业共享网盘。 Zoho Workdriv…

Android BlueToothBLE入门(三)——数据的分包发送和接收(源码已更新)

学更好的别人&#xff0c; 做更好的自己。 ——《微卡智享》 本文长度为3675字&#xff0c;预计阅读12分钟 前言 接上篇《Android BlueToothBLE入门&#xff08;二&#xff09;——设备的连接和通讯&#xff08;附Demo源码地址&#xff09;》最后提到过蓝牙BLE通讯每次默认发送…

qt 32位编译 内存溢出 无法 运行在win7 32位

项目在 编译32位系统 内存溢出 设置成了x64 但是 最后在xp32位系统运行提示 在下载了n个dll之后发现这种状况无穷无尽&#xff0c;后来在查阅资料时发现可以直接打开qt安装目录下的“vcredist”文件夹&#xff0c;将对应位数的程序拷到win7电脑上&#xff0c;直接运行&…

优化营商环境:构建智能营销平台,助力企业经营发展

对于企业来说&#xff0c;没有了客户&#xff0c;就像身体没有了血液&#xff0c;将失去生命力和活力&#xff0c;续存难发展更难。区域产业又是由一个个企业集聚而形成&#xff0c;企业的成败也就决定着区域产业的兴衰。 在当今竞争激烈的商业环境中&#xff0c;传统的销售手段…

基于python的爬虫实现

定义 爬虫&#xff08;Web crawler&#xff09;&#xff0c;也被称为网络爬虫、网络蜘蛛或网络机器人&#xff0c;是一种自动化程序&#xff0c;用于浏览互联网并收集网页内容。 基本原理 爬虫的工作原理是通过发送HTTP请求从网页服务器获取网页的内容&#xff0c;然后解析网…

何时使用Windbg静态分析?何时使用Windbg动态调试?

目录 1、概述 2、使用Windbg静态分析dump文件 2.1、异常捕获模块自动生成dump文件 2.2、从Windows任务管理器中导出dump文件 2.3、从正在动态调试的Windbg中使用命令导出dump文件 2.4、使用Windbg静态分析dump文件的一般步骤 3、使用Windbg动态调试目标进程 3.1、程序发…

Win10 配置NDK安装2023.7.19版本

NDK安装流程 1. 下载&#xff1a;2. 安装&#xff1a;3. 测试&#xff1a; 在大多数情况下&#xff0c;使用 Android SDK 管理器安装 NDK 会更轻松。本文单独安装NDK&#xff0c;但后续也可以使用管理器进行管理。 1. 下载&#xff1a; 地址 Fig.1 最新稳定版本 2. 安装&…

精益生产的五大管理工具:提升效率,降低成本!

在制造业的世界里&#xff0c;精益生产是一种以追求在制造过程的各个方面减少浪费为中心的方法。为了实现这一目标&#xff0c;有几个经常使用的管理工具。这些工具使制造商能够识别和消除生产过程中任何效率低下或浪费的资源。本文将讨论精益生产中使用的一些关键管理工具&…

Android 进程与进程之间的通信--Messager 详细教程,两个app实现

Messenger是一种轻量级的IPC方案,它的底层实现其实就是AIDL.跨进程通信使用Messenger时,Messenger会将所有服务调用加入队列,然后服务端那边一次处理一个调用,不会存在同时调用的情况.而AIDL则可能是多个调用同时执行,必须处理多线程问 步骤详情 一、服务端 public class MyM…