先知
邮箱的一些基本概念:
发送邮件
-
STMP协议:
-
Simple Mail Transfer Protocol ,简单邮箱传输协议,用于发送邮件的协议。
-
基于TCP,保证可靠性,但不安全,是明文传输
-
Spring Boot默认也是基于此协议进行发送邮件
-
接收邮件
-
POP3协议:
-
Post Office Protocol 3:邮局通信协议第三版,用于接收邮箱的标准协议。
-
“一次性取信”,客户端把邮件下载到本地之后,通常会从服务器上删除(可以配置保留副本)。如果没有设置,电脑上收了邮件,手机上就没法再看到这封邮件
-
-
IMAP协议:
-
Internet Message Access Protocol : 互联网消息访问协议,是POP3的代替,也是用于接收邮件的协议
-
“云端管理”:客户端拉去邮件,只是将“视图”同步到本地,不会删除,始终保存在服务器上。
-
Gmail、QQ邮箱、OutLook等现代邮箱,都是用的IMAP协议
-
邮箱的传送流程

当你用QQ邮箱向网易邮箱发送了一封邮件,会发生什么?
-
QQ邮箱客户端会使用SMTP协议将 邮件 发送到自家的邮件服务器上
-
QQ邮箱服务器,接收到一封邮件,然后会解析目标地址的域名
-
QQ邮箱服务器,识别到是其他服务器上的域名,就会进行转发,同样使用SMTP协议发送
-
网易邮箱服务器接收到一封邮件,发现是目标地址是自己的域名,就将信封存储在服务器上
-
网易邮箱客户器上线,查看服务器上有没有邮件,如果有,就拉取。使用IMAP或者POP3协议
邮箱开通第三方服务
-
如果你使用的是飞书邮箱
找到邮箱的位置,“第三方邮箱客户端登陆”

随便选择一个设备

生成,获取到授权码、用户名、发信服务器

-
如果你使用的是QQ邮箱
找到账户中心

开启POP3、IMAP等服务

选择你的验证方式,即可生成授权码

- 其他的网易邮箱等,获取授权码的方式类似
邮箱集成使用
说明:以下的使用,参考Spring中文网
-
依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
-
配置文件
spring: mail: # 指定邮箱的服务器地址 host: smtp.feishu.cn # 执行邮箱的发送者,由哪一个邮箱账号来发送邮件 username: huag****@ifree8.com # 邮件的授权码,邮箱账号需要开通第三方服务 password: 29F7s******** default-encoding: UTF-8
-
测试
@Test public void test() throws Exception { // 直接创建 JavaMailSenderImpl 实现类 JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); javaMailSender.setDefaultEncoding("utf-8"); javaMailSender.setHost("smtp.qq.com"); javaMailSender.setPort(465); javaMailSender.setUsername("747692844@qq.com"); javaMailSender.setPassword("<你的密码/授权码>"); javaMailSender.setProtocol("smtps"); // 创建一个邮件消息 MimeMessage message = javaMailSender.createMimeMessage(); // 创建 MimeMessageHelper MimeMessageHelper helper = new MimeMessageHelper(message, false); // 发件人邮箱和名称 helper.setFrom("747692844@qq.com", "springdoc"); // 收件人邮箱 helper.setTo("admin@springboot.io"); // 邮件标题 helper.setSubject("Hello"); // 邮件正文,第二个参数表示是否是HTML正文 helper.setText("Hello <strong> World</strong>!", true); // 发送 javaMailSender.send(message); }
类之间的关系
在测试类中可以看到,主要涉及的主要类就是:
-
JavaMailSender
-
MimeMessage
-
MimeMessageHelper
JavaMailSender
类的继承关系



-
底层实现自MaiSender接口,接口中抽象定义了send(),而JavaMailSenderImpl就是实现类
-
JavaMailSender的作用就是主要用来发送邮件的
-
抽象理解:真实发送快递的“快递员”
MimeMessage
有了“快递员”,我们还需要“快递”——Message
public class MimeMessage extends Message implements MimePart { ...... public void setFrom(Address address) throws MessagingException { if (address == null) { this.removeHeader("From"); } else { this.setAddressHeader("From", new Address[]{address}); } } ...... }
来看看里面都有哪些方法和属性

-
setFrom():设置邮箱展示的信息,比如名称和邮箱等
-
setRecipients():设置收件人的邮箱
-
setText():邮件内容
- 所以,Message的作用:主要是定义邮件内容、收件人的信息
MimeMessageHelper
Helper,“包装快递”的好帮手
看看Helper中提供了些什么方法?

为什么Helper中提供的主要方法跟Message中的方法差不多呢?
其实Helper就是用来帮助封装Message中的信息的
public class MimeMessageHelper { ...... public void setFrom(String from) throws MessagingException { Assert.notNull(from, "From address must not be null"); this.setFrom(this.parseAddress(from)); } public void setFrom(InternetAddress from) throws MessagingException { Assert.notNull(from, "From address must not be null"); this.validateAddress(from); this.mimeMessage.setFrom(from); } ...... }
public class MimeMessage extends Message implements MimePart { ...... public void setFrom(Address address) throws MessagingException { if (address == null) { this.removeHeader("From"); } else { this.setAddressHeader("From", new Address[]{address}); } } ...... }
- 可以看到,其实Helper提供的方法中,底层操作的对象,其实就是Message邮件信息
区别在于:
-
MimeMessage是底层的****API,能够做所有的事情
-
MimeMessage适用的参数大多是已经封装好的,比如setFrom()方法中的参数是Address,这个类包装了邮件的地址等很多信息。
-
MimeMessageHelper是提供的工具类,旨在于帮助用户更方便的设置Message的属性
-
MimeMessageHelper在设置复杂邮件内容(HTML 模板邮件 + 附件),会帮助用户省去很多封装的步骤,底层进行处理,简化代码。比如setFrom()方法中的参数是String,数据更加的原始。
- 所以,在面对更加复杂的场景,使用Helper能够帮助我们剩下很多力气
邮箱验证码案例
-
依赖
<dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <!--redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- Apache Commons Pool2 for Redis connection pooling --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!--redisson工具集--> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> </dependency> <!--Thymeleaf模板引擎--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--OGNL表达式语言--> <dependency> <groupId>ognl</groupId> <artifactId>ognl</artifactId> <version>3.3.4</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
-
使用配置类config读取配置文件,方便后续的使用
spring: mail: host: smtp.feishu.cn username: hua******@ifree8.com password: 29****** default-encoding: UTF-8 mailFrom: hua******@ifree8.com mailPersonal: 通用Ai智能体 mailSubject: 邮箱验证码 regExpr: ^(?!.*..)[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,}$ variable: code htmlTemplate: MailVerifyTemplate.html


-
生成模板

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>邮箱验证码</title> </head> <body style="font-family: Arial, Helvetica, sans-serif; background-color: #f6f8fa; padding: 20px;"> <div style="max-width: 600px; margin: auto; background: #ffffff; border-radius: 8px; padding: 30px; box-shadow: 0 2px 6px rgba(0,0,0,0.1);"> <h2 style="text-align: center; color: #333333;">邮箱验证码</h2> <p style="font-size: 16px; color: #333;">您好,</p> <p style="font-size: 14px; color: #555;"> 您正在进行邮箱验证,本次验证码如下(5分钟内有效): </p> <div style="text-align: center; margin: 30px 0;"> <span style="font-size: 28px; font-weight: bold; color: #2d89ef; letter-spacing: 3px;" th:text="${code}"> 88888 </span> </div> <p style="font-size: 14px; color: #555;"> 请在验证页面输入上方验证码完成验证。为保障账号安全,请勿将验证码告知他人。 </p> <hr style="margin: 30px 0; border: none; border-top: 1px solid #eee;"> <p style="font-size: 12px; color: #999; text-align: center;"> 如果这不是您本人的操作,请忽略此邮件。 </p> </div> </body> </html>
-
生成自定义的JavaMailSender

因为邮箱验证码的场景,邮箱发送者一般是固定不变的,所以初始化为Bean,使用的时候直接注入即可。
-
使用
public interface AuthService { /** * 发送邮箱验证码 * @param targetMail 接收方邮箱 * @return 执行结果响应体 */ ResponseEntity sendCode2Mail(String targetMail); /** * 验证邮箱验证码 * @param mail 验证的邮箱 * @param userCode 用户给的验证码 * @return 执行结果响应体 */ ResponseEntity verifyMailCode(String mail,String userCode); }
@Service @Slf4j public class AuthServiceImpl implements AuthService { //邮箱配置类 @Autowired private MailConfig mailConfig; //Sender ,“快递员” @Autowired @Qualifier("defaultJavaMailSender") private JavaMailSender javaMailSender; //Redis,操作工具类。其实就是RedisTemplate一些方法的封装而已。也可以直接使用RedisTemplate。 //具体不在这里详说,网上也有很多教程(其实就是懒,求原谅) @Autowired private RedisBase redisBase; //包含一些Key prefix,expire time等 @Autowired private RedisConstant redisConstant; @Override public ResponseEntity<String> sendCode2Mail(String targetMail) { //1. 校验邮箱 if (StrUtil.isBlank(targetMail)) { return ResponseEntity.failBusinessException(FAIL,"邮箱不能为空"); } if (!ReUtil.isMatch(mailConfig.getRegExpr(), targetMail)) { return ResponseEntity.failBusinessException(FAIL,"邮箱格式不正确"); } //2. 处理消息模板 //读取模板 TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("templates", TemplateConfig.ResourceMode.CLASSPATH)); Template template = engine.getTemplate(mailConfig.getHtmlTemplate()); //生成随机数字 int codeNum = NumberUtil.generateRandomNumber(200000, 999999,1)[0]; String code = String.valueOf(codeNum); //渲染模板的变量 String htmlContent = template.render(Map.of(mailConfig.getVariable(), code)); //3. 封装邮件 //创建消息 MimeMessage message = javaMailSender.createMimeMessage(); //创建helper MimeMessageHelper helper = null; //封装消息 try { helper = new MimeMessageHelper(message,false); helper.setFrom(mailConfig.getMailFrom(),mailConfig.getMailPersonal()); //邮件展示,哪个邮箱账号,名称 helper.setTo(targetMail);//邮件接收方 helper.setSubject(mailConfig.getMailSubject()); //邮件主题 helper.setText(htmlContent,true); //邮件内容 } catch (Exception e) { log.error("happen error:", e); return ResponseEntity.failBusinessException(FAIL,"发生异常,请稍后重试!"); } //4. 存储验证码到Redis boolean setnxFlag = redisBase.setnx(redisConstant.getMailVerifyCodeKeyPrefix() + targetMail, code, redisConstant.getMailVerifyCodeExpireSeconds()); if (!setnxFlag) { return ResponseEntity.failBusinessException(FAIL,"redis异常"); } //5. 发送邮件 javaMailSender.send(message); //6. 返回结果 return ResponseEntity.ok(null,"邮箱发送成功,请注意查收!"); } @Override public ResponseEntity verifyMailCode(String mail, String registMailCode) { //1. 使用邮箱从redis中获取验证码 String key = redisConstant.getMailVerifyCodeKeyPrefix()+mail; String systemCode = (String)redisBase.get(key); //2. 校验验证码 if (systemCode==null) { return ResponseEntity.failBusinessException(FAIL,"尚未存在邮箱验证码或验证码已过期,请重新发送邮箱验证码!"); } if (!StrUtil.equals(registMailCode,systemCode)) { return ResponseEntity.failBusinessException(FAIL,"验证码错误!"); } //3. 返回结果 return ResponseEntity.ok(); } }
总结:
到这,我们已经学习了:
-
邮箱协议的基本知识
-
邮箱的发送和接收,在客户端和服务端的流程过程
-
还知道了如何开通邮箱的第三方客户端使用服务
-
还使用SpringBoot集成了邮箱,能够使用代码的形式,向邮箱发送邮件
以上内容是我个人根据网上的资料进行学习和转化,如果有错误,欢迎指出,共同进步哦~
(第一次写博客,写得很烂,哈哈哈见谅,希望你能够从垃圾中,灵活抽取出有用的东西吧)