移动端双验证码登录实现

说明:本文介绍如何用图形验证码+短信验证码实现移动端登录思路;

分析

通过手机号+图形验证码+手机验证码实现登录的时序图如下:

在这里插入图片描述

说明:

  • (1)用户进入登录界面,出现图形验证码,可点击图形验证码更换图片;

  • (2)后端返回图形验证码的base64地址,加上一个uuid,该uuid为验证码在Redis中存储的Key;

  • (3)用户输入手机号、uuid、图形验证码,获取手机短信验证码;

  • (4)后端根据uuid去Redis中获取图形验证码,与用户输入的进行比较,通过发送短信验证码,同时将短信验证码的MessageId与验证码存入Redis中,不通过返回错误信息;

  • (5)用户输入手机号、uuid、messageId、图形验证码、手机验证码登录;

  • (6)后端根据uuid、messageId去Redis中获取验证码,分别与用户输入的验证码比较,通过登录成功,发Token,不通过返回错误信息;

前端实现

首先,做一个简单的页面,如下:

在这里插入图片描述

页面有三个接口,分别是:获取图形验证码,获取短信验证码,登录,代码如下:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>获取验证码</title><script src="js/axios-0.18.0.js"></script></head><body>输入手机号:<input type="text" id="phone"><br>输入图形验证码:<input type="text" id="img"><img id="pic" /><input type="button" value="获取图形验证码" onclick="getImg()"><br>输入短信验证码:<input type="text" id="msg"><input type="button" value="获取短信验证码" onclick="getMsg()"><br><p></p><input type="button" value="登录" onclick="login()">
</body>
<script>// 图形验证码返回的uuidlet uuid = "";// 短信验证码返回的msgIdlet msgId = "";function getImg() {// 异步交互ajaxaxios.get("http://localhost:8080/getImg").then(response => {// 接收响应回来的数据console.log(response.data);uuid = response.data.uuid;document.getElementById("pic").src = 'data:image/jpeg;base64,' + response.data.data;})}function getMsg() {// 手机号const phone = document.getElementById("phone").value;// 图形验证码const imgValue = document.getElementById("img").value;const data = {phone:phone,imgValue: imgValue,uuid: uuid};// 发送 POST 请求axios.post("http://localhost:8080/getMsg", data).then(response => {console.log("请求发送成功:", response.data);msgId = response.data.msgId}).catch(error => {console.error("请求发送失败:", error);});}function login() {// 手机号const phone = document.getElementById("phone").value;// 图形验证码const imgValue = document.getElementById("img").value;// 短信验证码const msgValue = document.getElementById("msg").value;// 构造登录请求的数据对象const loginData = {phone: phone,imgValue: imgValue,msgValue: msgValue,uuid: uuid,msgId: msgId};// 发送 POST 请求axios.post("http://localhost:8080/login", loginData).then(response => {console.log("登录成功:", response.data);}).catch(error => {console.error("登录失败:", error);});}
</script></html>

图像验证码使用使用Kaptcha实现,参考:

  • 使用Kaptcha生成验证码

后端实现

KaptchConfig类

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;import java.util.Properties;@Component
public class KaptchConfig {@Beanpublic DefaultKaptcha getDefaultKaptcha() {// 创建验证码工具com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha();// 验证码配置Properties properties = new Properties();// 图片边框properties.setProperty("kaptcha.border", "no");// 边框颜色properties.setProperty("kaptcha.border.color", "black");//边框厚度properties.setProperty("kaptcha.border.thickness", "1");// 图片宽properties.setProperty("kaptcha.image.width", "120");// 图片高properties.setProperty("kaptcha.image.height", "60");//图片实现类properties.setProperty("kaptcha.producer.impl", "com.google.code.kaptcha.impl.DefaultKaptcha");//文本实现类properties.setProperty("kaptcha.textproducer.impl", "com.google.code.kaptcha.text.impl.DefaultTextCreator");//文本集合,验证码值从此集合中获取properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");//验证码长度properties.setProperty("kaptcha.textproducer.char.length", "4");//字体properties.setProperty("kaptcha.textproducer.font.names", "宋体");//字体颜色properties.setProperty("kaptcha.textproducer.font.color", "black");//文字间隔properties.setProperty("kaptcha.textproducer.char.space", "4");//干扰实现类properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.DefaultNoise");//干扰颜色properties.setProperty("kaptcha.noise.color", "blue");//干扰图片样式properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");//背景实现类properties.setProperty("kaptcha.background.impl", "com.google.code.kaptcha.impl.DefaultBackground");//背景颜色渐变,结束颜色properties.setProperty("kaptcha.background.clear.to", "white");//文字渲染器properties.setProperty("kaptcha.word.impl", "com.google.code.kaptcha.text.impl.DefaultWordRenderer");// 创建验证码配置实例Config config = new Config(properties);// 验证码工具defaultKaptcha.setConfig(config);return defaultKaptcha;}
}

三个接口实现;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.TimeUnit;@RestController
@CrossOrigin
public class KaptchController {@ResourceDefaultKaptcha defaultKaptcha;@Autowiredprivate StringRedisTemplate redisTemplate;/*** 生成图形验证码*/@GetMapping("/getImg")public Map getImg() throws IOException {// 生成文字验证码String imageCode = defaultKaptcha.createText();// 生成图片验证码ByteArrayOutputStream out = new ByteArrayOutputStream();BufferedImage image = defaultKaptcha.createImage(imageCode);ImageIO.write(image, "jpg", out);// 生成uuid,将uuid作为key,验证码作为value存入redisString uuid = java.util.UUID.randomUUID().toString();redisTemplate.opsForValue().set(uuid, imageCode, 60, TimeUnit.SECONDS);// 对字节组Base64编码return Map.of("data", Base64.getEncoder().encodeToString(out.toByteArray()), "uuid", uuid, "imageCode", imageCode);}/*** 生成短信验证码*/@PostMapping("/getMsg")public Map getCode(@RequestBody Map<String, String> map) throws IOException {// 获取相关参数String phone = map.get("phone");String uuid = map.get("uuid");String imgValue = map.get("imgValue");// 根据uuid获取图形验证码String imageCode = redisTemplate.opsForValue().get(uuid);// 校验手机号是否合法if (phone == null || phone.length() != 11) {return Map.of("data", "手机号不合法");}// 图形验证码是否过期if (imageCode == null) {return Map.of("data", "验证码已过期");}// 是否输入正确if (!imageCode.toUpperCase().equals(imgValue.toUpperCase())) {return Map.of("data", "验证码错误");}// 生成6位数的短信验证码String msgCode = String.valueOf((int) ((Math.random() * 9 + 1) * 100000));// 生成msgId,将msgId作为key,验证码作为value存入redisString msgId = java.util.UUID.randomUUID().toString();redisTemplate.opsForValue().set(msgId, msgCode, 60, TimeUnit.SECONDS);return Map.of("data", msgCode, "msgId", msgId);}/*** 登录*/@PostMapping("/login")public Map login(@RequestBody Map<String, String> map) {// 获取相关参数String phone = map.get("phone");String uuid = map.get("uuid");String imgValue = map.get("imgValue");String msgId = map.get("msgId");String msgValue = map.get("msgValue");// 根据uuid、msgId获取图形验证码、短信验证码String imageCode = redisTemplate.opsForValue().get(uuid);String msgCode = redisTemplate.opsForValue().get(msgId);// 校验手机号是否合法if (phone == null || phone.length() != 11) {return Map.of("data", "手机号不合法");}// 图形验证码是否过期if (imageCode == null) {return Map.of("data", "验证码已过期");}// 是否输入正确if (!imageCode.toUpperCase().equals(imgValue.toUpperCase())) {return Map.of("data", "验证码错误");}// 短信验证码是否过期if (msgCode == null) {return Map.of("data", "验证码已过期");}// 是否输入正确if (!msgCode.equals(msgValue)) {return Map.of("data", "验证码错误");}// 登录成功,删除Redis中的验证码redisTemplate.delete(uuid);redisTemplate.delete(msgId);return Map.of("data", "success");}
}

测试

测试正常情况

在这里插入图片描述

测试图形验证码输入错误的情况

在这里插入图片描述

测试短信验证码输入错误的情况(图中“登录成功”为接口访问成功的信息,并非通过了登录校验)

在这里插入图片描述

基本实现了,正式情况还需要考虑更严格的手机号校验,手机验证码防频繁点击,手机短信登录第三方API接入,规范验证码在Redis中Key的格式,验证码在Redis中的过期时间等等,这里仅是一个Demo,但上述实现思路是值得考虑的。

总结

本文介绍了移动端双验证码登录的实现,希望能对大家有所启发。

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

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

相关文章

探索Java设计模式:享元模式

探索Java设计模式&#xff1a;深入理解与实践享元模式 享元模式&#xff08;Flyweight Pattern&#xff09;是一种结构型设计模式&#xff0c;它通过共享对象来有效地支持大量细粒度对象的复用&#xff0c;从而降低系统内存占用&#xff0c;提高性能。在Java编程中&#xff0c…

【2024】深度学习配置环境常见报错,持续更新中....

No such file or directory: ‘:/usr/local/cuda-10.1:/usr/local/cuda-10.1/bin/nvcc‘: 确保CUDA正常后&#xff0c;直接运行以下代码&#xff1a; export CUDA_HOME/usr/local/cuda详细原因参照&#xff0c;安装mmcv-full时报错&#xff1a;Could not build wheels for mm…

python多线程技术(Threading)

文章目录 前言一、多线程(Threading)是什么?二、threading库1.初识多线程2.增加新线程2.1 多线程的基本使用2.2 对多线程是同时进行的进行一个直观上的演示(非重点--理解是实时就行)2.3 thread.join()功能2.4 使用queue(队列)功能获取多线程的返回值(重要,这就是前面那…

R version 4.1.0 安装ggplot2,options(pkgType=“binary“) install.packages(“ggplot2“)

R version 4.1.0 安装ggplot2 R version 4.1.0 安装ggplot21. 报错信息2. 成功安装3. 参考资料 R version 4.1.0 安装ggplot2 # 直接安装报错 install.packages("ggplot2")# 安装成功 options(pkgType"binary") install.packages("ggplot2")1. …

浅析ARM Contex-CM3内核架构

目录 概述 1. Cortex-M3类型MCU 1.1 MCU 架构 1.2 实时性系统概念 1.3 处理器命名法 1.4 MCU的一些知识 2. Cortex-M3 概览 2.1 Cortex-M3综述 2.2 寄存器组 2.3 操作模式和特权极别 2.4 内建的嵌套向量中断控制器 2.5 存储器映射 2.6 总线接口 2.7 存储器保护单元…

SpringBoot多数据源(一)

SpringBoot多数据源&#xff08;一&#xff09; 1.多数据源使用场景1.1 业务复杂&#xff08;数据量大&#xff09;1.2 读写分离 2.多数据源配置3.应用4.测试 1.多数据源使用场景 1.1 业务复杂&#xff08;数据量大&#xff09; 简单理解就是业务量复杂&#xff0c;将庞大的数…

Python模块之logging

官方文档 常见用法 logging模块是Python标准库中用于记录日志的模块。它提供了灵活且可配置的日志记录功能&#xff0c;可以用于在应用程序中捕获和输出各种级别的日志消息。以下是logging模块的常见用法示例&#xff1a; python import logging# 配置日志记录器 logging.b…

Java中的==与equals的区别

区别 首先&#xff0c;是运算符&#xff0c;equals()是方法 是一个二元运算符&#xff0c;用于比较两个对象的引用是否相同。当使用 进行比较时&#xff0c;Java会检查两个对象的内存地址是否相同。如果两个对象的内存地址相同&#xff0c;则认为它们是相同的。 如果比较的…

浏览器原理之本地存储

一 浏览器本地存储方式及使用场景 Cookies: 小段数据&#xff0c;由服务器发送到用户浏览器&#xff0c;浏览器将其存储并在以后的请求中回送到服务器。使用场景&#xff1a;会话管理&#xff08;登录状态、用户偏好设置&#xff09;、个性化&#xff08;用户主题&#xff09;、…

本地消息表模式保障分布式系统最终一致性

系统架构说明 状态转换说明 订单表消息表process_queue库存系统return_queue说明成功失败///订单库回滚成功成功失败//订单系统重发消息成功成功成功失败/Broker自动重试&#xff0c;注意接口幂等成功成功成功库存不足退回/Broker通知回掉&#xff0c;订单/消息作废成功成功成…

车轮上的智能:探索机器学习在汽车行业的应用前景

文章目录 引言&#xff1a;一、机器学习在汽车设计中的应用设计优化模拟与测试 二、智能制造与生产三、自动驾驶技术感知与决策数据融合 四、市场与模式的变革五、机器学习对于汽车行业的机遇与挑战挑战机遇 引言&#xff1a; 在当今数字化时代&#xff0c;机器学习作为人工智…

速盾:cdn可以加速哪些服务器

CDN&#xff08;Content Delivery Network&#xff0c;内容分发网络&#xff09;是一种通过将网站的静态资源部署到全球各地的服务器上&#xff0c;以提供更快速、更可靠的访问体验的技术。CDN可以加速许多类型的服务器&#xff0c;包括但不限于以下几种&#xff1a; 静态资源服…

全方位解析:深入了解Microsoft Edge浏览器的优势与特性

目录 1. 速度快&#xff1a; 2. 内存占用低&#xff1a; 3. 集成性好&#xff1a; 4. 支持Web标准&#xff1a; 5. 定制化选项&#xff1a; 6. 阅读模式和笔记功能&#xff1a; 7. 搜索引擎优化&#xff1a; 8. 扩展程序库&#xff1a; 9. 跨平台同步&#xff1a; 10…

android学习笔记(四)

1、ExampleInstrumentedTest的解读&#xff1a; /*** Instrumented test, which will execute on an Android device.** see <a href"http://d.android.com/tools/testing">Testing documentation</a>*/ RunWith(AndroidJUnit4.class) public class Exa…

Kafka -基本概念

认识Kafka kafka是一个多分区、多副本、基于zookeeper协调的分布式消息系统。 扮演角色 消息系统存储系统&#xff1a;把消息持久化到磁盘&#xff0c;相比于其他基于内存存储的系统而言&#xff0c;有效降低了数据丢失的风险。流式处理平台 基本概念 kafka的体系结构&…

Python爬虫-批量爬取Manner Coffee全国门店

前言 本文是该专栏的第25篇,后面会持续分享python爬虫干货知识,记得关注。 在本专栏之前,笔者在文章《Python爬虫-批量爬取星巴克全国门店》里面有详细介绍采集全国门店的方法以及完整代码思路。对此领域感兴趣的同学,可以点击翻阅查看即可。 而在本文中,笔者将详细来介…

【离散数学】关系

一、序偶和笛卡尔积 序偶&#xff1a;两个元素按照一定的次序组成的二元组&#xff0c;记为<x,y>&#xff0c;x为第一元素&#xff0c;y为第二元素 序偶的相等条件&#xff1a;<a,b><c,d>当且仅当ac,bd n重有序组&#xff1a;n个元素按照一定次序组成的n元…

前端css中table表格的属性使用

前端css中table表格的属性使用 一、前言二、常见的表格属性1.边框的样式2.布局和对齐3.间距和填充4.背景和颜色5.字体的样式6.边框的圆角 三、简单的表格&#xff0c;例子11.源码12.源码1效果截图 四、给表格添加动画效果&#xff0c;例子21.源码22.源码2的运行效果 五、结语六…

kkFileView 任意文件上传致远程代码执行漏洞复现(QVD-2024-14703)

0x01 产品简介 kkFileView 是使用 spring boot 搭建的文件文档在线预览解决方案,支持主流办公文档的在线预览。 0x02 漏洞概述 2024年4月,互联网上披露kkFileView远程代码执行漏洞情报,攻击者可利用该漏洞上传恶意文件,获取操作系统权限。该漏洞利用简单,建议受影响的客…

大数据学习的第三天

文章目录 学习大数据命令的方式查看文件拷贝文件的方式添加数据的方式 出现了问题移动文件 hadoop工作流程和工作机制的方式namenodedatanodesecondarynamenode(主节点) 学习大数据命令的方式 查看文件 hadoop fs -cat /test/2.txt下载文件 hadoop fs -get -f /test/2.txt-f …