SpringBoot整合Vue3实现发送邮箱验证码功能
ztj100 2025-05-30 19:04 22 浏览 0 评论
1.效果演示
2.思维导图
3.前言
本篇文章主要讲解 Springboot 整合 Vue3 实现校验 qq 邮箱验证码之后重置用户密码的功能。
开发功能之前我们先梳理一下主要逻辑:
1.我们要想让系统给其他用户发送邮件,那么系统肯定要先绑定一个已注册的用户 A 的邮箱。 用户 A 必须开通 qq 邮箱的相关服务。开通服务的 A 的邮箱号码作为发件人。
2.在前端页面用户输入邮箱号码,点击获取验证码按钮调用后台接口,后台随机生成几个数字的验证码,并将该用户的邮箱号码和验证码存入 Redis。
3.用户输入收到的验证码。点击下一步调用后台校验验证码功能。后台将用户输入的验证码和 Redis 里面的数据进行比对,校验无误返回 true。验证失败返回错误的提示信息。
4.用户验证码校验无误之后,就进入到修改密码的页面,接着完成修改密码的功能。
4.开通 qq 邮箱服务
我们要让 A 用户给其他用户发送邮件,那么用户 A 必须开通相关的服务。
1.进入 qq 邮箱,点击右上角的账号与安全
2.开启邮箱服务,保存生成的邮箱授权码
5.前端
5.1 创建并配置 vue 项目
这里我们使用的前端脚手架是 vite,vue 版本是 vue3,前端组件库是 Element plus。
5.1.1 创建 vue3 项目
npm create vite@latest vue3-zhifou -- --template vue
5.1.2 安装配置 Element Plus
npm install element-plus --save
在 main.js 里面配置 Element plus
5.1.3 安装配置 axios
npm i axios -- save
在 src/util 下面新建 axios.js 文件
import axios from "axios";
import router from "../router/index"
import { ElMessage } from 'element-plus'
// 1. 创建axios实例
const instance = axios.create({
// 接口
baseURL: "/api",
// 超时时间
timeout: 60000,
});
// 2.请求拦截
instance.interceptors.request.use(
config => {
// let token = sessionStorage.getItem('token');
// if (token) {
// config.headers['token'] = token
// }
return config;
},
error => {
// 请求发生错误,抛出异常
Promise.reject(error);
}
);
// 3.响应拦截
instance.interceptors.response.use(
res => {
// 关闭进度条
return res;
},
error => {
// 关闭进度条
if (error && error.response) {
const status = error.response.status
switch (status) {
case 400:
ElMessage.error("请求错误");
break;
case 401:
ElMessage.error("未授权,请重新登录");
break;
case 404:
ElMessage.error("请求错误,未找到相应的资源");
break;
case 500:
ElMessage.error("服务器错误");
break;
default:
ElMessage.error("请求失败");
}
} else {
if (JSON.stringify(error).includes("timeout")) {
error.code = "TIMEOUT";
error.message = "服务器响应超时,请刷新页面";
}
}
return Promise.reject(error);
}
);
// 4.导出 axios 实例
export default instance;
5.1.4 封装常用的 http 请求
在 /src/util 下面新建 http.js 文件
import instance from "./axios";
const post = (url, data) => {
return new Promise((resolve, reject) => {
instance
.post(url, data)
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
};
const get = (url, data) => {
return new Promise((resolve, reject) => {
instance
.get(url, { params: data })
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
};
const put = (url, data) => {
return new Promise((resolve, reject) => {
instance
.put(url, data)
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
};
const del = (url, data) => {
return new Promise((resolve, reject) => {
instance
.delete(url, { params: data })
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
};
export default {
post,
get,
put,
del,
};
5.1.5 配置后端服务 IP 与端口
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path';
export default defineConfig({
plugins: [vue()],
// 设置别名
resolve: {
alias: [
{
// 设置别名, '@' 指向 'src' 目录
find: "@",
replacement: path.resolve(__dirname, './src')
},
]
},
server: {
open: true,
port: 3000,
proxy: {
"/api": {
target: "http://127.0.0.1:8081/springboot-vue3-email", //
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
})
5.1.6 配置访问后台的接口文件
在 /src/api 文件夹下新建接口文件:index.js
import http from "../utils/http";
// 发送邮箱验证码
const sendEmailVerifyCode = (data) => {
return http.get("/index/sendEmailVerifyCode", data);
};
// 校验邮箱验证码
const checkEmailVerifyCode = (data) => {
return http.post("/index/checkEmailVerifyCode", data);
};
// 通过邮箱重置用户密码
const resetPasswordByEmail = (data) => {
return http.post("/sysUser/resetPasswordByEmail", data);
};
export default { sendEmailVerifyCode, resetPasswordByEmail, checkEmailVerifyCode }
5.2 创建发送邮箱验证码的组件
在 /src/components 文件夹下新建 resetPassword.vue 文件。这里不再贴出完整代码,只讲核心代码,后面会有完整代码。
其实整个页面稍微有点复杂的就是发送验证码的功能
当用户点击获取验证码按钮,获取验证码这几个文字变成“x秒后重新发送”,并且禁止点击。
所以要定义一个数字变量用来显示倒计时,还要定义一个变量和 disabled 进行绑定。
const isSend = ref(false);
const countDown = ref(0);
用户点击获取验证码按钮之后调用方法:
当后台发送验证码成功之后,disabled 绑定的变量值就为 true,countDown 初始值为 60。
然后通过 setInterval 定时器每隔一秒将 countDown 的值减去 1。当 countDown 的值小于等于 0 时,disabled 绑定的变量值就为 false,countDown 的值变为 0,并清除定时器。
接着点击下一步按钮,校验验证码是否正确:
// 校验邮箱验证码
const checkEmailCode = () => {
emailFormRef.value.validate(async (valid) => {
if (valid) {
const res = await userApi.checkEmailVerifyCode(form);
if (res.data.code === 200) {
// 校验通过
if (res.data.data) {
isCheckEmail.value = true;
}
} else {
ElMessage.error(res.data.message);
}
} else {
return false;
}
});
};
当校验通过之后就显示重置用户密码的表单信息,否则提示校验失败的提示信息。
这里用户发送邮箱验证码和重置密码的表单项都在同一个 el-form 里面,只不过是通过一个变量的值进行控制显示。
默认 isCheckEmail 的值是 false,当验证码校验通过之后就修改为 true。
6.后端 6.1 创建 Springboot 项目
6.1 pom 文件引入相关依赖
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.4</version>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.15</version>
</dependency>
<!-- 邮箱验证码 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
6.2 yml 配置 email 相关属性
server:
port: 8081
servlet:
context-path: /springboot-vue3-email
spring:
# 数据库相关
datasource:
url: jdbc:mysql://127.0.0.1:3306/zc_online_order?allowPublicKeyRetrieval=true&useSSL=false
username: root
password: zhiFou2024@!
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# redis相关
redis:
host: 127.0.0.1
port: 6379
password: zhiFou2024@!
database: 0
# 邮箱相关
mail:
host: smtp.qq.com #邮箱服务器地址
username: 2xxxxxx@qq.com #邮箱账号
password: xxxxxxxx #邮箱授权码
default-encoding: utf-8 #默认编码
6.3 创建 Email 工具类
/**
* @author 知否技术
* @description 邮箱工具类
* @date 2024-09-25 10:31
*/
@Component
@Slf4j
public class EmailUtil {
@Autowired
private JavaMailSender javaMailSender;
@Autowired
private RedisTemplate redisTemplate;
/**
* 发件人邮箱
*/
@Value("${spring.mail.username}")
private String username ;
/**
* 发送邮箱验证码
* @param email
* @return
*/
public boolean sendEmailCode(String email){
// 创建邮件消息
SimpleMailMessage message = new SimpleMailMessage();
String emailCode = RandomUtil.randomNumbers(6);
// 邮箱验证码存入redis
String redisKey = String.format("redis:resetPassword:sendMessage:%s", email);
redisTemplate.opsForValue().set(redisKey, emailCode, 20, TimeUnit.MINUTES);
// 设置邮件主题
message.setSubject("【知否技术】你此次重置密码的验证码是:" + emailCode);
// 设置邮件发送者,昵称+<邮箱地址>
message.setFrom("发件人" + '<' + username + '>');
// 设置邮件接收者,可以有多个接收者,多个接受者参数需要数组形式
message.setTo(email);
// 设置邮件发送日期
message.setSentDate(new Date());
// 设置邮件的正文
message.setText("你此次重置密码的邮箱验证码是" + emailCode + ",请在20分钟内输入验证码进行下一步操作。如非本人操作,请忽略本次邮件!");
try {
// 发送邮件
javaMailSender.send(message);
return Boolean.TRUE;
} catch (Exception e) {
log.error("邮箱验证码异常结果: " + e.getMessage());
return Boolean.FALSE;
}
}
/**
* 校验邮箱验证码
* @param checkEmailParam
* @return
*/
public boolean checkEmailVerifyCode(CheckEmailParam checkEmailParam) {
String redisKey = String.format("redis:resetPassword:sendMessage:%s", checkEmailParam.getEmail());
// 从Redis获取邮箱验证码
String redisEmailCode = (String)redisTemplate.opsForValue().get(redisKey);
if(StrUtil.isBlank(redisEmailCode)){
throw new RuntimeException("验证码已过期,请重新发送!");
}
if(!checkEmailParam.getVerifyCode().equals(redisEmailCode)){
throw new RuntimeException("验证码不正确,请重新填写!");
}
return true;
}
}
6.4 封装 Redis 配置类
新建 RedisConfig 类
@Slf4j
@Configuration
public class RedisConfig {
@Resource
private RedisConnectionFactory factory;
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
6.5 配置全局异常处理器
新建 GlobalExceptionHandler 类
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 捕获全局异常
*
* @param e
* @return
*/
@ExceptionHandler(RuntimeException.class)
public Result handler(RuntimeException e) {
log.error("全局异常:{}", e.getMessage());
return Result.fail(e.getMessage());
}
}
6.6 创建 controller
@RestController
@RequestMapping("/index")
public class IndexController {
@Autowired
private EmailUtil emailUtil;
/**
* 发送邮箱验证码
* @param email
* @return
*/
@GetMapping("/sendEmailVerifyCode")
public Result sendEmailVerifyCode(@RequestParam String email) {
boolean flag = emailUtil.sendEmailCode(email);
if (flag) {
return Result.success(Boolean.TRUE);
} else {
return Result.fail();
}
}
/**
* 校验邮箱验证码
* @param checkEmailParam
* @return
*/
@PostMapping("/checkEmailVerifyCode")
public Result checkEmailVerifyCode(@RequestBody CheckEmailParam checkEmailParam){
boolean flag = emailUtil.checkEmailVerifyCode(checkEmailParam);
if (flag) {
return Result.success(Boolean.TRUE);
} else {
return Result.fail();
}
}
}
7.完整代码
链接:https://pan.baidu.com/s/1bYaQwdnsw8O8hmXTGI8iVQ?pwd=1234
提取码:1234
相关推荐
- 这个 JavaScript Api 已被废弃!请慎用!
-
在开发过程中,我们可能会不自觉地使用一些已经被标记为废弃的JavaScriptAPI。这些...
- JavaScript中10个“过时”的API,你的代码里还在用吗?
-
JavaScript作为一门不断发展的语言,其API也在持续进化。新的、更安全、更高效的API不断涌现,而一些旧的API则因为各种原因(如安全问题、性能瓶颈、设计缺陷或有了更好的替代品)被标记为“废...
- 几大开源免费的 JavaScript 富文本编辑器测评
-
MarkDown编辑器用的时间长了,发现发现富文本编辑器用起来是真的舒服。...
- 比较好的网页里面的 html 编辑器 推荐
-
如果您正在寻找嵌入到网页中的HTML编辑器,以便用户可以直接在网页上编辑HTML内容,以下是几个备受推荐的:CKEditor:CKEditor是一个功能强大的、开源的富文本编辑器,可以嵌入到...
- Luckysheet 实现excel多人在线协同编辑
-
前言前些天看到Luckysheet支持协同编辑Excel,正符合我们协同项目的一部分,故而想进一步完善协同文章,但是遇到了一下困难,特此做声明哈,若侵权,请联系我删除文章!若侵犯版权、个人隐私,请联系...
- 从 Element UI 源码的构建流程来看前端 UI 库设计
-
作者:前端森林转发链接:https://mp.weixin.qq.com/s/ziDMLDJcvx07aM6xoEyWHQ引言...
- 手把手教你如何用 Decorator 装饰你的 Typescript?「实践」
-
作者:Nealyang转发连接:https://mp.weixin.qq.com/s/PFgc8xD7gT40-9qXNTpk7A...
- 推荐五个优秀的富文本编辑器
-
富文本编辑器是一种可嵌入浏览器网页中,所见即所得的文本编辑器。对于许多从事前端开发的小伙伴来说并不算陌生,它的应用场景非常广泛,平时发个评论、写篇博客文章等都能见到它的身影。...
- 基于vue + element的后台管理系统解决方案
-
作者:林鑫转发链接:https://github.com/lin-xin前言该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统(WebManagementSystem)开发。基于v...
- 开源富文本编辑器Quill 2.0重磅发布
-
开源富文本编辑器Quill正式发布2.0版本。官方TypeScript声明...
- Python之Web开发框架学习 Django-表单处理
-
在Django中创建表单实际上类似于创建模型。同样,我们只需要从Django类继承,则类属性将是表单字段。让我们在myapp文件夹中添加一个forms.py文件以包含我们的应用程序表单。我们将创建一个...
- Django测试入门:打造坚实代码基础的钥匙
-
这一篇说一下django框架的自动化测试,...
- Django ORM vs SQLAlchemy:到底谁更香?从入门到上头的选择指南
-
阅读文章前辛苦您点下“关注”,方便讨论和分享,为了回馈您的支持,我将每日更新优质内容。...
- 超详细的Django 框架介绍,它来了!
-
时光荏苒,一晃小编的Tornado框架系列也结束了。这个框架虽然没有之前的FastAPI高流量,但是,它也是小编的心血呀。总共16篇博文,从入门到进阶,包含了框架的方方面面。虽然小编有些方面介绍得不是...
- 20《Nginx 入门教程》使用 Nginx 部署 Python 项目
-
今天的目标是完成一个PythonWeb项目的线上部署,我们使用最新的Django项目搭建一个简易的Web工程,然后基于Nginx服务部署该PythonWeb项目。1.前期准备...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)