个人开发者实现邮箱发送验证码
在本文中,我们将基于springboot,实现邮箱发送验证码的功能,用于后续企业级项目的集成。
本文记录了笔者基于spring-boot-starter-mail依赖包和smtp(网易)服务器做的邮箱验证码发送功能。
(本项目针对没有邮箱服务器的个人开发者)
(本项目要求:开发者拥有redis服务,如何安装redis本文不再介绍)
一、概述与前置条件
在这个部分,笔者将简单介绍一下邮箱协议与网易邮箱开通协议的申请流程(其他服务如QQ邮箱同理。)
1.1邮箱协议概述(可以不看)
| 协议 | 描述 |
| POP3 (Post Office Protocol - Version 3) | 协议用于支持使用电子邮件客户端获取并删除在服务器上的电子邮件。 |
| IMAP (Internet Message Access Protocol) | 协议用于支持使用电子邮件客户端交互式存取服务器上的邮件。 |
| SMTP (Simple Mail Transfer Protocol) | 协议用于支持使用电子邮件客户端发送电子邮件。 |
1.2IMAP 和 POP 协议的区别
1、POP允许电子邮件客户端下载服务器上的邮件,但是你在电子邮件客户端上的操作(如:移动邮件、标记已读等)不会反馈到服务器上的,比如:你通过电子邮件客户端收取了QQ邮箱中的3封邮件并移动到了其他文件夹,这些移动动作是不会反馈到服务器上的,也就是说,QQ邮箱服务器上的这些邮件是没有同时被移动的。需要特别注意的是,第三方客户端通过POP收取邮件时,也是有可能同步删除服务端邮件。在第三方客户端设置 POP 时,请留意是否有 保留邮件副本/备份 相关选项。如有该选项,且要保留服务器上的邮件,请勾选该选项。
2、IMAP协议下电子邮件客户端的操作都会反馈到服务器上,你对邮件进行的操作(如:移动邮件、标记已读、删除邮件等)服务器上的邮件也会做相应的动作。也就是说,IMAP 是“双向”的。同时,IMAP 可以只下载邮件的主题,只有当你真正需要的时候,才会下载邮件的所有内容。
3、在 POP3 和 IMAP 协议上,推荐使用IMAP协议来存取服务器上的邮件。
1.2 授权码及其申请(必看)
邮箱授权码不等于你的邮箱密码。邮箱授权码的权限低于你的邮箱密码的权限,授权码仅仅可以用于登录以下服务:POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV 服务。
(特别注意,每当你修改密码之后,你的授权码都会改变!!!改变!!!改变!!!)
授权码申请流程(以网易邮箱举例)
首先我们需要登录我们的网易邮箱,在设置中开启我们的邮箱服务。
第一步,进入邮箱,点击设置,点击POP3/SMTP/IMAP选项。

第二步,开通权限,笔者这里已经开通完成,这里我们主要看三个点即可。
1. 将IMAP/POP3/SMTP服务开启
2. 记住授权码(一定要复制到文件里!!!后面查看不了授权码的)
3. 记住网易邮箱的服务器地址

二、项目搭建
在邮箱验证码功能实现开始之前,我们需要对项目功能需要进行分析。
2.1 项目功能简介
1. 可以给(目标邮箱)客户端发送验证码,服务器可以校验验证码。
2. 对同一用户,短时间内只发送一次验证码。
3. 每个验证码只能使用一次。
4. 每个验证码的有效期是15min。
2.2 idea中新建项目
在idea中新建一个Spring Initializr项目。
2.2 项目依赖介绍
1. JDK版本 V1.8
2. springboot 版本 V2.6.13
3. spring-boot-starter-mail V2.6.13
4. spring-boot-starter-data-redis V4.1.84
5. spring-data-redis V2.7.13
pom文件如下:
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.7.13</version>
</dependency>
</dependencies>
在功能实现开始之前,我们先编写一个独立的配置文件,用于设定一些参数,这里是笔者将配置参数写在了application.properties中,具体如下。
#服务器端地址(发送验证码方的域名)
spring.mail.host: smtp.163.com
#(服务器端口号)
spring.mail.port: 465
#(登录邮箱服务器下用户名)简单来说就是你的邮箱地址)
spring.mail.username: **********@163.com
#(登录邮箱服务器的密码)对个人开发者来说就是
#(以网易举例)就是你邮箱开通SMTP协议后申请的授权码。
spring.mail.password: **************
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.ssl.enable=true
# 这个字段需要与spring.mail.username相同
#(发送验证码的邮箱号,接收方收到的邮件的发送方为此用户名)
spring.mail.properties.mail.smtp.from=huangjiacheng9418@163.com
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
# redis配置,用于实现存CODE、验CODE、过期时间
spring.redis.host: localhost
spring.redis.port: 6379
spring.redis.timeout: 5000ms
spring.redis.jedis.pool.max-active: 8
spring.redis.jedis.pool.max-idle: 8
spring.redis.jedis.pool.min-idle: 0
这里我们需要将之前授权码的相关参数填入配置文件中。
三、功能开发
在前面两步中我们完成了依赖导入和配置文件的编写,在这里我们进行核心逻辑的开发部分。
3.1 邮箱发送的核心Service的实现
1. 新建一个EmailService接口,定义我们的邮箱发送接口类。
public interface EmailService {
public void sendSimpleMessage(String targetAddr,String Code);
}
(为什么这样子设计接口?)
(首先,目标邮箱地址targetAddr是前端发送给controller层,再传给我们service层,这个毋庸置疑。)
(其次就是验证码Code,这个验证码的生成要在controller层生成,原因是我们要和redis交互,redis中有没有目标用户的未使用的验证码来决定要不要生成Code验证码)
2. 在包下新建一个Impl文件夹,在里面新建一个EmailServiceImpl的实现类,实现接口。
@Service
public class EmailServiceImpl implements EmailService {
@Autowired
private JavaMailSender javaMailSender;
//发送邮箱验证码的逻辑
public void sendSimpleMessage(String targetAddr,String Code) {
SimpleMailMessage message = new SimpleMailMessage();
//目标(用户邮箱)
message.setTo(targetAddr);
//邮件主题
//message.setSubject(Code);
message.setSubject(SUBJECT_LOGIN);
//短信全文
String text = emailCONST.PRE_TEXT + Code + emailCONST.AFTER_TEXT;
//String text = "你的验证码为:" + Code + ",该验证码 15 分钟内有效,请勿泄漏于他人。";
message.setText(text);
javaMailSender.send(message);
}
}
个人开发中直接使用字符串没什么问题,但是为了简化维护,我们把这些文本都设置为静态常量。
3. 在包下新建一个eamilConst.java文件,声明一些常量值。
public class emailConst {
//邮箱正文前缀
public static final String PRE_TEXT = "请确认登录操作,您的验证码为:";
//邮箱正文后缀,对应验证码有效期的值
public static final String AFTER_TEXT = ",该验证码 15 分钟内有效,请勿泄漏于他人。";
//验证码有效期(redis过期时间)
public static final Long CODE_EXPIRE_TIME = 15*60;
//redis中存放验证码的key的前缀
public static final String REDIS_PREFIX = "EMAILCODE_";
//邮箱主题
public static final String SUBJECT_LOGIN = "****登录通知";
}
3.2 邮件验证码controller编写
这里我们开始编写controller,创建EmailController.java编写。
@RestController
@RequestMapping("/email")
public class EmailController {
@Autowired
private EmailServiceImpl emailService;
@Autowired
private RedisService redisService;
/*
//请求发送邮箱验证码
*userid 为了简化开发,用于标识redis中的KEY
*targetAddr 要发送的邮箱地址
*/
@PostMapping("/sendCode")
public String SendValidateCode(String userId,String targetAddr)
{
String redis_user_key = emailCONST.REDIS_PREFIX+userId;
//如果redis中存在了未使用的邮箱验证码
if(redisService.getCode(redis_user_key) != null)
{
return "验证码已发送,请稍后再试";
}else{//第一次申请邮箱验证码
String Code = GetRandomCode();
emailService.sendSimpleMessage(targetAddr,Code);
redisService.saveCode(redis_user_key,Code);
return "验证码发送成功";
}
}
@PostMapping("/validate")
//邮箱验证码CODE验证
public String validate(int userId, String Code)
{
//验证码处理逻辑
String redis_user_key = emailCONST.REDIS_PREFIX+userId;
if(Code.equals(redisService.getCode(redis_user_key))){
redisService.deleteCode(redis_user_key);
return "验证成功";
}else{
return "验证码错误";
}
}
//验证码生成函数
public String GetRandomCode(){
int minn=0;
int maxn=999999;
int Code = ThreadLocalRandom.current().nextInt(minn,maxn+1);
//转换为6位数字的格式
return String.format("%06d",Code);
}
}
上述文件中拷贝进去会提示找不到RedisService,这是因为他是一个java文件,存放了是redis操作的相关文件。下一步我们引入redis相关操作的service。
3.3 redis操作的service层
这一步中,首先我们新建RedisService接口文件,其内容如下:
package com.wisdom.utils.domain.service;
import org.springframework.stereotype.Service;
@Service
public interface RedisService {
public void saveCode(String id,String Code);
public String getCode(String id);
public boolean deleteCode(String id);
}
第二步,我们在刚刚的Impl文件夹中建立实现类RedisServiceIpml实现类实现RedisService接口。
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private StringRedisTemplate redisTemplate;
//保存邮箱验证码
@Override
public void saveCode(String CodeKey, String CodeValue) {
redisTemplate.opsForValue().set(CodeKey,CodeValue, emailCONST.CODE_EXPIRE_TIME, TimeUnit.SECONDS);
}
//获取邮箱验证码
@Override
public String getCode(String CodeKey) {
return redisTemplate.opsForValue().get(CodeKey);
}
//删除邮箱验证码
@Override
public boolean deleteCode(String CodeKey) {
return redisTemplate.delete(CodeKey);
}
}
至此,我们的邮箱验证码的模块就开发完毕了,微调一下项目结构+增加业务需要,就可以集成在项目中了。
四、功能测试
如果想要直观一点的页面,我们既可以选择编写前端来进行测试,也可以选择利用swagger来进行测试,但是为了简便性,这里我们直接使用接口测试工具apifox进行测试。
首先,启动服务。
第一步,以一号用户请求发送验证码的URL,显示“验证码发送成功”。
(邮箱中确实接收到邮件)

同一用户二次请求邮箱验证码,会提示“验证码已发送,请稍后再试”。

切换为校验验证码的URL,检测验签结果,错误的验证码会提示“验证码错误”,正确的验证码提示“验证通过”。

每个验证码的有效期写在常量文件中,当前是15min的过期时间。
五、结语
至此,我们在SpringBoot项目中完成了邮箱验证码发送的功能。
更多推荐



所有评论(0)