当前位置: 首页 > news >正文

使用邮箱验证实现登录功能(发送邮件,redis)

目录

概述 

前端搭建

 后端搭建

生成验证码-存入redis(主要过程代码)

发送邮件(主要过程代码)

登录验证-取出redis中数据进行验证(主要代码)

完整代码一-LoginController

完整代码二-LoginService

完整代码三-LoginInfo

功能测试


概述 

*使用邮箱进行登录验证功能实现。

主要功能点:

1.在SpringBoot环境中发送邮件。

2.使用redis进行验证码的缓存及取出验证登录

 整体过程说明 

前端搭建简易邮箱登录框,当验证通过后自动跳转到网页index.html。

后端提供两个接口,一个接口用于处理向指定邮箱发送验证码,并将验证码存入redis中,另一个接口用于处理登录验证,验证发来的邮箱地址和验证码是否匹配。

 技术选型 

前端:前端三剑客,axios 

后端:SpringBoot ,redis,javaMail

前端搭建

 项目结构 

 页面展示 

 说明 当我们输入邮箱地址后,点击"发送验证码",会判断输入是否为空以及输入的邮箱是否符合规范,如果输入不为空并且输入的邮箱符合规范,那么按钮将禁用并且提示再次发送的倒计时。

例如:

 

  前端完整代码实例 --- js

// 获取发送验证码按钮节点
let sendCodeBtn = document.querySelector("#sendCodeBtn");
// 获取邮箱地址输入框节点
let emailInput = document.querySelector("#userEmail");
// 获取验证码输入框节点
let verificationCode = document.querySelector("#CheckCode");
// 获取登录按钮节点
let loginBtn = document.querySelector("#loginBtn");// 点击发送验证码逻辑代码
sendCodeBtn.addEventListener("click",function(){// 判断用户输入的邮箱地址是否符合规范let flag = CheckInputEmail(emailInput.value);// 以及判断输入是否为空if(emailInput.value && flag){// 调用方法发送请求,向目标邮箱发送验证码getCode(emailInput.value);alert("验证码发送成功!")// 禁用发送按钮sendCodeBtn.disabled = true;// 设置倒计时,时间到后才可以再次发起验证码请求let time = 60;sendCodeBtn.innerHTML =  time + "s后再次发送!"time--;let myInterVal =  setInterval(()=>{if(time != 0){sendCodeBtn.innerHTML = time + "s后再次发送!"time--;}else{sendCodeBtn.disabled = false;sendCodeBtn.innerHTML = "发送"clearInterval(myInterVal)}},1000)}else{alert("邮箱格式错误或为空!");}
})// 点击登录按钮逻辑代码
loginBtn.addEventListener("click", function (){// 输入邮箱地址和验证码作为参数进行验证checkCode(emailInput.value,verificationCode.value);
})// 验证邮箱输入格式是否正确
function CheckInputEmail(email){if (!email) {return false;}// 定义邮箱格式的正则表达式const emailRegex = /^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;// 检查邮箱是否符合格式要求return emailRegex.test(email);
}// 发送请求:获取验证码
function getCode(emailAddress){axios.get("http://localhost:8080/logins?emailAddress="+emailAddress)
}// 发送请求:检查验证码是否正确
function checkCode(emailAddress, code){axios.post("http://localhost:8080/logins",{emailAddress:emailAddress,code:code}).then(response =>{ // 成功的回调console.log("返回结果====>" + response.data)response.data? window.location = "../html/index.html" : alert("验证不通过!!!");})
}

 前端完整代码实例 --- html

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title></title><link rel="stylesheet" href="../css/login.css"><link href="../favicon.ico" rel="shortcut icon"><script src="../js/axios.min.js"></script>
</head>
<body>
<div id="container"><div id="my_header"><span id="header_title">邮箱登录</span></div><div id="my_body"><div id="body_inputBlock"><label for="userEmail">邮箱:</label><input id="userEmail" type="text" placeholder="请输入邮箱"><button id="sendCodeBtn" type="button">发送验证码</button><br><br><label for="CheckCode">验证码:</label><input id="CheckCode" type="password" placeholder="请输入验证码"><br><button id="loginBtn">登录</button></div></div>
</div>
<script src="../js/login.js"></script>
</body>
</html>

  前端完整代码实例 --- css

#container{border: solid #589bff 2px;width: 500px;height: 250px;margin: 50px auto;border-radius: 5px;background-color: #7acc67;
}#my_header{background-color: #bcffc1;height: 50px;line-height: 50px;text-align: center;font-weight: 800;font-size: 20px;
}#my_body{margin-top: 15px;margin-left: 35px;
}#loginBtn{font-weight: 800;font-size: 18px;margin-top: 20px;margin-left: 153px;width: 101px;
}label{width: 104px;display: inline-block;text-align: right;
}
input{border-radius: 5px;height: 27px;width: 212px;background-color: antiquewhite;
}
label{font-size: 18px;
}button{font-size: 15px;border-radius: 5px;background-color: antiquewhite;height: 36px;
}button:hover{cursor: pointer;
}#sendCodeBtn{position: relative;top: 1px;font-weight: 600;
}

 后端搭建

 目录结构 

生成验证码-存入redis(主要过程代码)

1.我们获取时间戳的后四位来模拟随机验证码。

// 截取当前时间戳的后四位模拟验证码
long timestamp = System.currentTimeMillis();
String verificationCode = Long.toString(timestamp).substring(Long.toString(timestamp).length() - 4);

2.有了随机的验证码,我们导入redis起步坐标,并简单配置连接信息。

<!--pom.xml导入redis起步坐标-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--application.yml中编写-->
redis:host: localhost <!--redis地址-->port: 6379 <!--redis开放端口号-->

3.使用-loginController中编写

/*** 注入redisTemplate用于缓存数据*/
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 将传入邮箱地址为key,验证码为value,存入redis中,并设置超时时间为5分钟
// 参数说明set(key,value, 超时时间,时间单位)
// 至于传入的详细参数,下方会有完整代码提供解释
redisTemplate.opsForValue().set(emailAddress,verificationCode,5, TimeUnit.MINUTES);

发送邮件(主要过程代码)

1.获取授权码 

 说明 :我们想要在编程环境发送邮件,首先需要到我们自己所使用的邮箱中开启POP3/SMTP服务后获取授权密码。

 在此我使用的是网易的163邮箱,开启服务并获取授权密码步骤:

开启任意一个,根据步骤发送个短信后就出现授权码了。 

 

 2.编写邮件发送信息(需要授权码)

在application.yml中编写

 3.编写发送邮件方法-在loginService中编写邮件发送逻辑

// 自动注入JavaMailSender,用于发送邮件动作
@Autowired
private JavaMailSender javaMailSender;
 /*** 发送邮件* @param from 发送方* @param to 接收方* @param subject 主题* @param contents 内容* @return 返回执行结果状态*/
public void sendEmail(String from, String to, String subject, String contents){// 创建一个简单消息对象,用于发送简单消息(不带附件或连接等)SimpleMailMessage simpleMailMessage = new SimpleMailMessage();// 封装邮件信息simpleMailMessage.setFrom(from); // 设置发送方simpleMailMessage.setTo(to); // 设置接收方simpleMailMessage.setSubject(subject); // 设置主题simpleMailMessage.setText(contents); // 设置发送内容// 发送动作javaMailSender.send(simpleMailMessage);
}

4.调用方法-loginController中编写

/*** 注入loginService用于调用其中自定义的邮件发送方法*/
@Autowired
private LoginService loginService;
// 编写邮件内容模板
String emailInfo = "验证码为:" + verificationCode +",5分钟内有效。";// 发送验证码到目标邮箱
loginService.sendEmail("maohe101@163.com", emailAddress, "登录验证码", emailInfo);

登录验证-取出redis中数据进行验证(主要代码)

loginController中编写 

// 取出传入数据对比redis中数据,判断是否通过登录验证
// 判断传入的邮箱地址是否存在redis中
if(redisTemplate.hasKey(loginInfo.getEmailAddress())){// 取出对应邮箱地址为key的value值String storyCode = redisTemplate.opsForValue().get(loginInfo.getEmailAddress());// 判断存在redis中的验证码和传入验证码是否相同,相同返回truereturn loginInfo.getCode().equals(storyCode)? "true":"false";
}
return "false";

完整代码一-LoginController

package com.mh.controller;import com.mh.pojo.LoginInfo;
import com.mh.service.LoginService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;import java.util.Date;
import java.util.concurrent.TimeUnit;/*** Date:2023/2/28* author:zmh* description: 登录页面相关接口**/
@Slf4j // 用于输出日志
@RestController
@RequestMapping("/logins")
public class LoginController {/*** 注入redisTemplate用于缓存数据*/@Autowiredprivate RedisTemplate<String, String> redisTemplate;/*** 注入loginService用于调用其中自定义的邮件发送方法*/@Autowiredprivate LoginService loginService;/*** 发送邮件* @param emailAddress 目标邮件地址,作为redis存储的key*/@GetMappingpublic void sendEmail(String emailAddress){log.info("====>请求参数邮箱地址为{}",emailAddress);// 1.生成4位随机验证码// 截取当前时间戳的后四位模拟验证码long timestamp = System.currentTimeMillis();String verificationCode = Long.toString(timestamp).substring(Long.toString(timestamp).length() - 4);log.info("====>验证码为:{}",verificationCode);// 2.存入redis// 将传入邮箱地址为key,验证码为value,存入redis中,并设置超时时间为5分钟redisTemplate.opsForValue().set(emailAddress,verificationCode,1, TimeUnit.MINUTES);// 3.编写邮件内容模板String emailInfo = "验证码为:" + verificationCode +",5分钟内有效。";// 4.发送验证码到目标邮箱loginService.sendEmail("maohe101@163.com", emailAddress, "登录验证码", emailInfo);}/*** 登录验证* @param loginInfo 接收前端传入的邮箱地址和code* @return 返回执行结果*/@PostMappingpublic String CheckEmailCode(@RequestBody LoginInfo loginInfo){log.info("=====>POST请求接收到的参数:{}***{}",loginInfo.getEmailAddress(), loginInfo.getCode());// 取出传入数据对比redis中数据,判断是否通过登录验证// 判断传入的邮箱地址是否存在redis中if(redisTemplate.hasKey(loginInfo.getEmailAddress())){// 取出对应邮箱地址为key的value值String storyCode = redisTemplate.opsForValue().get(loginInfo.getEmailAddress());// 判断存在redis中的验证码和传入验证码是否相同,相同返回truereturn loginInfo.getCode().equals(storyCode)? "true":"false";}return "false";}}

完整代码二-LoginService

package com.mh.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;/*** Date:2023/2/28* author:zmh* description: 登录模块相关业务**/@Service
public class LoginService {@Autowiredprivate JavaMailSender javaMailSender;/*** 发送邮件* @param from 发送方* @param to 接收方* @param subject 主题* @param contents 内容* @return 返回执行结果状态*/public void sendEmail(String from, String to, String subject, String contents){// 创建一个简单消息对象,用于发送简单消息(不带附件或连接等)SimpleMailMessage simpleMailMessage = new SimpleMailMessage();// 封装邮件信息simpleMailMessage.setFrom(from);simpleMailMessage.setTo(to);simpleMailMessage.setSubject(subject);simpleMailMessage.setText(contents);// 发送动作javaMailSender.send(simpleMailMessage);}}

完整代码三-LoginInfo

package com.mh.pojo;import lombok.Data;/*** Date:2023/2/28* author:zmh* description: 登录信息-用于接收前端传入的body数据**/@Data
public class LoginInfo {String emailAddress;String code;}

功能测试

说明:在测试之前我们可以关闭一下浏览器缓存,方便我们测试。F12打开控制台

输入邮箱获取验证码

 邮箱接收到验证码

 输入错误验证码,进行对比验证

  输入正确验证码,进行对比验证 

 验证成功,自动跳转到index.heml网页,功能测试完成。

相关文章:

使用邮箱验证实现登录功能(发送邮件,redis)

目录 概述 前端搭建 后端搭建 生成验证码-存入redis&#xff08;主要过程代码&#xff09; 发送邮件&#xff08;主要过程代码&#xff09; 登录验证-取出redis中数据进行验证&#xff08;主要代码&#xff09; 完整代码一-LoginController 完整代码二-LoginService 完…...

【Linux】网卡的7种bond模式

网卡的7种bond模式 一、bond模式 Mode0(balance-rr) 表示负载分担round-robin&#xff0c;和交换机的聚合强制不协商的方式配合 Mode1(active-backup) 表示主备模式&#xff0c;只有一块网卡是active,另外一块是备的standby&#xff0c;这时如果交换机配的是捆绑&#xff0c…...

AQS抽象队列同步器

aqs 抽象队列同步器&#xff0c;内部存储了一个valitail修饰的status 和内部类node &#xff0c;来实现对共享变量并发同步队列机制,以reentrantLock为例&#xff0c;lock底层实际上调用的是sync的lock&#xff0c;会调用cas对status的状态进行修改&#xff0c;来确定是否获得锁…...

springBoot对REST支持源码解析

一、在配置类中&#xff1a; AutoConfiguration(after { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.class }) ConditionalOnWebApplication(type Type.SERVLET) ConditionalOnClass({ Servlet.class, D…...

6 集成学习及Python实现

1 主要思想 集成学习: 三个臭裨将, 顶个诸葛亮 Bagging: 数据随机重抽样, 并行构建分类器, 投票&#xff1b;Boosting: 关注被错分的样本, 串行构建分类器, 加权投票。 2 理论 AdaBoost (Adaptive Boosting)示意图1 错误率: εEN\varepsilon \frac{E}{N}εNE​ 其中NNN为…...

如何编程实现从多数据库操作数据

对于数据量很大的复杂系统&#xff0c;有时候会采用分库或者分表的减轻单台数据库服务器压力&#xff0c;截止目前有一些工具直接支持读写分离等&#xff0c;例如ShardingSphere&#xff0c;如果不采用工具框架&#xff0c;从编码出发&#xff0c;如何实现从多个数据库读写数据…...

LeetCode 147. 对链表进行插入排序 | C/C++版

LeetCode 147. 对链表进行插入排序 | C语言版LeetCode 147. 对链表进行插入排序题目描述解题思路思路一&#xff1a;使用栈代码实现运行结果参考文章&#xff1a;思路二&#xff1a;减少遍历节点数代码实现运行结果参考文章&#xff1a;[]()LeetCode 147. 对链表进行插入排序 …...

【QT进阶】第五章 QT绘图之自定义控件--仪表盘绘制

❤️作者主页:凉开水白菜 ❤️作者简介:共同学习,互相监督,热于分享,多加讨论,一起进步! ❤️专栏目录:【零基础学QT】文章导航篇 ❤️专栏资料:https://pan.baidu.com/s/192A28BTIYFHmixRcQwmaHw 提取码:qtqt ❤️点赞 👍 收藏 ⭐再看,养成习惯 订阅的粉丝可通过…...

Java代码弱点与修复之——URL manipulation(URL操纵)

弱点描述 “URL manipulation” 是指攻击者利用应用程序中的 URL 参数来执行恶意操作的一种攻击技术。 在 URL manipulation 攻击中,攻击者会修改应用程序中的 URL 参数,以便执行不当操作,如访问未授权的页面、修改他人的数据、绕过访问控制等。攻击者通常会使用手动修改 …...

Sharding Sphere学习

一、基本概念 1.什么是Sharding Sphere 2.分库分表3.分库分表的方式 4.分库分表应用和问题 5.功能 5.1数据分片 —核心概念 —使用限制 5.2分布式事务 —核心概念 —使用限制 5.3读写分离 —核心概念 —使用限制 5.4高可用 —核心概念 —使用限制 5.5数据库网关 —核心概念…...

粗心小编被云拯救,那云上数据谁来拯救?

开工第三天      小编已忙的焦头烂额      不是因为工作积压      而是因为自己的疏忽      也许是没有进入工作状态,一大早先经历了自行车钥匙丢失,手机遗落在家,好不容易坐到工位上才发现,备份数据的U盘忘带了。    不过,好在提前将工作文件上传到了云端…...

[git可视化软件]gitkraken平替:GitAhead

日期2023-02-28 gitkraken6.5.1已经不能登陆使用了!! 6.5.1免费版已经无法使用!!! 现在是2023-02-28 这款工具已经废除了6.5.1版本的使用功能了,我直接卡在登陆界面进不去项目了. 要想继续管理私有项目,只能升级最新版的软件,并且开通会员.会员费用高的一批,一年要59.4美元.约…...

CentOS8基础篇8:使用systemctl管理NFS服务

一、服务简介 服务&#xff1a;是指执行指定系统功能的程序、例程或进程&#xff0c;以便支持其他程序&#xff0c;尤其是底层(接近硬件)程序。 例如&#xff1a;打印服务&#xff0c;ftp服务&#xff0c;http服务。 服务就是一个程序&#xff08;正在执行的程序&#xff09…...

Go defer用法

defer概览 defer是go语言里的一个关键字,在 函数内部使用;defer关键字后面跟一个 函数或匿名函数; defer用法 执行一些资源的收尾工作,如 关闭数据库连接,关闭文件描述符,释放资源等等;结合recover()函数使用,防止函数内部的异常导致整个程序停止;defer在遇到panic后,仍然会…...

产地证是什么,主要作用有哪些?

产地证是什么&#xff0c;主要作用有哪些&#xff1f;最近一个客户问我&#xff0c;产地证是什么&#xff0c;主要作用有哪些&#xff1f;今天就来扒拉扒拉这个问题&#xff0c;其实很简单~通俗一点的讲&#xff0c;产地证是货物原产地的证明文件之一&#xff0c;主要用于国外清…...

王道计算机网络课代表 - 考研计算机 第一章 计算机网络体系结构 究极精华总结笔记

本篇博客是考研期间学习王道课程 传送门 的笔记&#xff0c;以及一整年里对 计算机网络 知识点的理解的总结。希望对新一届的计算机考研人提供帮助&#xff01;&#xff01;&#xff01; 关于对 “计算机网络体系结构” 章节知识点总结的十分全面&#xff0c;涵括了《计算机网络…...

数据处理 |遍历所有文件夹及子目录文件夹方法总结与实例代码详解

深度学习中不可避免的数据预处理~1. glob.glob()方法 2. pathlib中的Path方法3. os.walk()方法1. glob.glob()方法 语法glob.glob(pathname)&#xff08;多指定文件类型&#xff0c;查找jpg,png,txt,json等&#xff09;缺点&#xff1a;查找文件较慢2. 路径操作库pathlib中的Pa…...

ProtoEditor - 如何在Unity中实现一个Protobuf通信协议类编辑器

文章目录简介Protobuf 语法规则Proto Editor实现创建窗口定义类、字段增删类编辑字段导入、导出Json文件生成.proto文件生成.bat文件简介 在Socket网络编程中&#xff0c;假如使用Protobuf作为网络通信协议&#xff0c;需要了解Protobuf语法规则、编写.proto文件并通过编译指令…...

2022 OpenCV Spatial AI大赛前三名项目分享,开源、上手即用,优化了OAK智能双目相机的深度效果。

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 ▌前言 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是助手…...

Android 蓝牙开发——HCI log 分析(二十)

HCI log 是用来分析蓝牙设备之间的交互行为是否符合预期,是否符合蓝牙规范。对于蓝牙开发者来说,通过 HCI log 可以帮助我们更好地分析问题,理解蓝牙协议。 一、抓取HCI log 1、手机抓取HCI log 在开发者选项中打开启用蓝牙HCI信息收集日志开关,Android系统就开始自动地收…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

flow_controllers

关键点&#xff1a; 流控制器类型&#xff1a; 同步&#xff08;Sync&#xff09;&#xff1a;发布操作会阻塞&#xff0c;直到数据被确认发送。异步&#xff08;Async&#xff09;&#xff1a;发布操作非阻塞&#xff0c;数据发送由后台线程处理。纯同步&#xff08;PureSync…...

文件上传漏洞防御全攻略

要全面防范文件上传漏洞&#xff0c;需构建多层防御体系&#xff0c;结合技术验证、存储隔离与权限控制&#xff1a; &#x1f512; 一、基础防护层 前端校验&#xff08;仅辅助&#xff09; 通过JavaScript限制文件后缀名&#xff08;白名单&#xff09;和大小&#xff0c;提…...

Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合

无论是python&#xff0c;或者java 的大型项目中&#xff0c;都会涉及到 自身平台微服务之间的相互调用&#xff0c;以及和第三发平台的 接口对接&#xff0c;那在python 中是怎么实现的呢&#xff1f; 在 Python Web 开发中&#xff0c;FastAPI 和 Django 是两个重要但定位不…...

跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践

在电商行业蓬勃发展的当下&#xff0c;多平台运营已成为众多商家的必然选择。然而&#xff0c;不同电商平台在商品数据接口方面存在差异&#xff0c;导致商家在跨平台运营时面临诸多挑战&#xff0c;如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...

云原生安全实战:API网关Envoy的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口&#xff0c;负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...