找回密码
 立即注册
首页 业界区 业界 Spring Boot接入邮箱,完成邮箱验证码

Spring Boot接入邮箱,完成邮箱验证码

全叶农 2025-9-26 16:04:32
先知

邮箱的一些基本概念:

发送邮件


  • STMP协议:

    • Simple Mail Transfer Protocol ,简单邮箱传输协议,用于发送邮件的协议。
    • 基于TCP,保证可靠性,但不安全,是明文传输
    • Spring Boot默认也是基于此协议进行发送邮件

接收邮件


  • POP3协议:

    • Post Office Protocol 3:邮局通信协议第三版,用于接收邮箱的标准协议。
    • 一次性取信”,客户端把邮件下载到本地之后,通常会从服务器上删除(可以配置保留副本)。如果没有设置,电脑上收了邮件,手机上就没法再看到这封邮件

  • IMAP协议:

    • Internet Message Access Protocol : 互联网消息访问协议,是POP3的代替,也是用于接收邮件的协议
    • 云端管理”:客户端拉去邮件,只是将“视图”同步到本地,不会删除,始终保存在服务器上。
    • Gmail、QQ邮箱、OutLook等现代邮箱,都是用的IMAP协议

邮箱的传送流程

1.png

当你用QQ邮箱向网易邮箱发送了一封邮件,会发生什么?

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


  • 如果你使用的是飞书邮箱
找到邮箱的位置,“第三方邮箱客户端登陆”
2.png

随便选择一个设备
3.png

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


  • 如果你使用的是QQ邮箱
找到账户中心
5.png

开启POP3、IMAP等服务
6.png

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


  • 其他的网易邮箱等,获取授权码的方式类似
邮箱集成使用

说明:以下的使用,参考Spring中文网

  • 依赖
  1. <dependency>   
  2.     <groupId>org.springframework.boot</groupId>   
  3.     spring-boot-starter-mail</artifactId>
  4. </dependency>
复制代码

  • 配置文件
  1. spring:
  2.   mail:
  3.   # 指定邮箱的服务器地址
  4.     host: smtp.feishu.cn
  5.     # 执行邮箱的发送者,由哪一个邮箱账号来发送邮件
  6.     username: huag****@ifree8.com
  7.     # 邮件的授权码,邮箱账号需要开通第三方服务
  8.     password: 29F7s********
  9.     default-encoding: UTF-8
  10.   
复制代码

  • 测试
  1. @Test
  2. public void test() throws Exception {
  3.     // 直接创建 JavaMailSenderImpl 实现类
  4.     JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
  5.     javaMailSender.setDefaultEncoding("utf-8");
  6.     javaMailSender.setHost("smtp.qq.com");            
  7.     javaMailSender.setPort(465);                       
  8.     javaMailSender.setUsername("747692844@qq.com");   
  9.     javaMailSender.setPassword("<你的密码/授权码>");     
  10.     javaMailSender.setProtocol("smtps");
  11.     // 创建一个邮件消息
  12.     MimeMessage message = javaMailSender.createMimeMessage();
  13.     // 创建 MimeMessageHelper
  14.     MimeMessageHelper helper = new MimeMessageHelper(message, false);
  15.     // 发件人邮箱和名称
  16.     helper.setFrom("747692844@qq.com", "springdoc");
  17.     // 收件人邮箱
  18.     helper.setTo("admin@springboot.io");
  19.     // 邮件标题
  20.     helper.setSubject("Hello");
  21.     // 邮件正文,第二个参数表示是否是HTML正文
  22.     helper.setText("Hello <strong> World</strong>!", true);
  23.     // 发送
  24.     javaMailSender.send(message);
  25. }
复制代码
类之间的关系

在测试类中可以看到,主要涉及的主要类就是:

  • JavaMailSender
  • MimeMessage
  • MimeMessageHelper
JavaMailSender

类的继承关系
8.png

9.png

10.png


  • 底层实现自MaiSender接口,接口中抽象定义了send(),而JavaMailSenderImpl就是实现类
  • JavaMailSender的作用就是主要用来发送邮件
  • 抽象理解:真实发送快递的“快递员”
MimeMessage

有了“快递员”,我们还需要“快递”——Message
  1. public class MimeMessage extends Message implements MimePart {
  2.     ......
  3.     public void setFrom(Address address) throws MessagingException {
  4.         if (address == null) {
  5.             this.removeHeader("From");
  6.         } else {
  7.             this.setAddressHeader("From", new Address[]{address});
  8.         }
  9.    
  10.     }
  11.    
  12.     ......
  13. }
复制代码
来看看里面都有哪些方法和属性
11.png


  • setFrom():设置邮箱展示的信息,比如名称和邮箱等
  • setRecipients():设置收件人的邮箱
  • setText():邮件内容


  • 所以,Message的作用:主要是定义邮件内容、收件人的信息
MimeMessageHelper

Helper,“包装快递”的好帮手
看看Helper中提供了些什么方法?
12.png

为什么Helper中提供的主要方法跟Message中的方法差不多呢?
其实Helper就是用来帮助封装Message中的信息的
  1. public class MimeMessageHelper {
  2.     ......
  3.    
  4.     public void setFrom(String from) throws MessagingException {
  5.         Assert.notNull(from, "From address must not be null");
  6.         this.setFrom(this.parseAddress(from));
  7.     }
  8.     public void setFrom(InternetAddress from) throws MessagingException {
  9.         Assert.notNull(from, "From address must not be null");
  10.         this.validateAddress(from);
  11.         this.mimeMessage.setFrom(from);
  12.     }
  13.    
  14.     ......
  15. }
复制代码
  1. public class MimeMessage extends Message implements MimePart {
  2.     ......
  3.     public void setFrom(Address address) throws MessagingException {
  4.         if (address == null) {
  5.             this.removeHeader("From");
  6.         } else {
  7.             this.setAddressHeader("From", new Address[]{address});
  8.         }
  9.    
  10.     }
  11.    
  12.     ......
  13. }
复制代码

  • 可以看到,其实Helper提供的方法中,底层操作的对象,其实就是Message邮件信息
区别在于:

  • MimeMessage底层的****API,能够做所有的事情
  • MimeMessage适用的参数大多是已经封装好的,比如setFrom()方法中的参数是Address,这个类包装了邮件的地址等很多信息。
  • MimeMessageHelper是提供的工具类,旨在于帮助用户更方便的设置Message的属性
  • MimeMessageHelper在设置复杂邮件内容(HTML 模板邮件 + 附件),会帮助用户省去很多封装的步骤,底层进行处理,简化代码。比如setFrom()方法中的参数是String,数据更加的原始。


  • 所以,在面对更加复杂的场景,使用Helper能够帮助我们剩下很多力气
邮箱验证码案例


  • 依赖
  1.                 org.springframework.boot        spring-boot-starter-web                org.springframework.boot        spring-boot-starter-test        <dependency>   
  2.     <groupId>org.springframework.boot</groupId>   
  3.     spring-boot-starter-mail</artifactId>
  4. </dependency>                    org.springframework.boot        spring-boot-starter-data-redis                    org.apache.commons        commons-pool2                    org.redisson        redisson-spring-boot-starter                    cn.hutool        hutool-all                    org.springframework.boot        spring-boot-starter-thymeleaf                    ognl        ognl        3.3.4                    org.projectlombok        lombok   
复制代码

  • 使用配置类config读取配置文件,方便后续的使用
  1. spring:
  2.   mail:
  3.     host: smtp.feishu.cn
  4.     username: hua******@ifree8.com
  5.     password: 29******
  6.     default-encoding: UTF-8
  7.     mailFrom: hua******@ifree8.com
  8.     mailPersonal: 通用Ai智能体
  9.     mailSubject: 邮箱验证码
  10.     regExpr: ^(?!.*\.\.)[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$
  11.     variable: code
  12.     htmlTemplate: MailVerifyTemplate.html
复制代码
13.png

14.png


  • 生成模板
15.png
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>邮箱验证码</title>
  6. </head>
  7. <body >
  8.     <h2 >邮箱验证码</h2>
  9.     <p >您好,</p>
  10.     <p >
  11.         您正在进行邮箱验证,本次验证码如下(5分钟内有效):
  12.     </p>
  13.    
  14.       
  15.         88888
  16.       
  17.    
  18.     <p >
  19.         请在验证页面输入上方验证码完成验证。为保障账号安全,请勿将验证码告知他人。
  20.     </p>
  21.     <hr >
  22.     <p >
  23.         如果这不是您本人的操作,请忽略此邮件。
  24.     </p>
  25. </body>
  26. </html>
复制代码

  • 生成自定义的JavaMailSender
16.png

因为邮箱验证码的场景,邮箱发送者一般是固定不变的,所以初始化为Bean,使用的时候直接注入即可。

  • 使用
  1. public interface AuthService {
  2.     /**
  3.      * 发送邮箱验证码
  4.      * @param targetMail 接收方邮箱
  5.      * @return 执行结果响应体
  6.      */
  7.     ResponseEntity sendCode2Mail(String targetMail);
  8.     /**
  9.      * 验证邮箱验证码
  10.      * @param mail 验证的邮箱
  11.      * @param userCode  用户给的验证码
  12.      * @return 执行结果响应体
  13.      */
  14.     ResponseEntity verifyMailCode(String mail,String userCode);
  15. }
复制代码
  1. @Service
  2. @Slf4j
  3. public class AuthServiceImpl implements AuthService {
  4.     //邮箱配置类
  5.     @Autowired
  6.     private MailConfig mailConfig;   
  7.     //Sender ,“快递员”
  8.     @Autowired
  9.     @Qualifier("defaultJavaMailSender")
  10.     private JavaMailSender javaMailSender;  
  11.     //Redis,操作工具类。其实就是RedisTemplate一些方法的封装而已。也可以直接使用RedisTemplate。
  12.     //具体不在这里详说,网上也有很多教程(其实就是懒,求原谅)
  13.     @Autowired
  14.     private RedisBase redisBase;  
  15.     //包含一些Key prefix,expire time等
  16.     @Autowired
  17.     private RedisConstant redisConstant;  
  18.     @Override
  19.     public ResponseEntity<String> sendCode2Mail(String targetMail) {
  20.         //1. 校验邮箱
  21.         if (StrUtil.isBlank(targetMail)) {
  22.             return ResponseEntity.failBusinessException(FAIL,"邮箱不能为空");
  23.         }
  24.         if (!ReUtil.isMatch(mailConfig.getRegExpr(), targetMail)) {
  25.             return ResponseEntity.failBusinessException(FAIL,"邮箱格式不正确");
  26.         }
  27.         //2. 处理消息模板
  28.         //读取模板
  29.         TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("templates", TemplateConfig.ResourceMode.CLASSPATH));
  30.         Template template = engine.getTemplate(mailConfig.getHtmlTemplate());
  31.         //生成随机数字
  32.         int codeNum = NumberUtil.generateRandomNumber(200000, 999999,1)[0];
  33.         String code = String.valueOf(codeNum);
  34.         //渲染模板的变量
  35.         String htmlContent = template.render(Map.of(mailConfig.getVariable(), code));
  36.         //3. 封装邮件
  37.         //创建消息
  38.         MimeMessage message = javaMailSender.createMimeMessage();
  39.         //创建helper
  40.         MimeMessageHelper helper = null;
  41.         //封装消息
  42.         try {
  43.             helper = new MimeMessageHelper(message,false);
  44.             helper.setFrom(mailConfig.getMailFrom(),mailConfig.getMailPersonal()); //邮件展示,哪个邮箱账号,名称
  45.             helper.setTo(targetMail);//邮件接收方
  46.             helper.setSubject(mailConfig.getMailSubject()); //邮件主题
  47.             helper.setText(htmlContent,true); //邮件内容
  48.         } catch (Exception e) {
  49.             log.error("happen error:", e);
  50.             return ResponseEntity.failBusinessException(FAIL,"发生异常,请稍后重试!");
  51.         }
  52.         //4. 存储验证码到Redis
  53.         boolean setnxFlag = redisBase.setnx(redisConstant.getMailVerifyCodeKeyPrefix() + targetMail, code, redisConstant.getMailVerifyCodeExpireSeconds());
  54.         if (!setnxFlag) {
  55.             return ResponseEntity.failBusinessException(FAIL,"redis异常");
  56.         }
  57.         //5. 发送邮件
  58.         javaMailSender.send(message);
  59.         //6. 返回结果
  60.         return ResponseEntity.ok(null,"邮箱发送成功,请注意查收!");
  61.     }
  62.     @Override
  63.     public ResponseEntity verifyMailCode(String mail, String registMailCode) {
  64.         //1. 使用邮箱从redis中获取验证码
  65.         String key = redisConstant.getMailVerifyCodeKeyPrefix()+mail;
  66.         String systemCode = (String)redisBase.get(key);
  67.         //2. 校验验证码
  68.         if (systemCode==null) {
  69.             return ResponseEntity.failBusinessException(FAIL,"尚未存在邮箱验证码或验证码已过期,请重新发送邮箱验证码!");
  70.         }
  71.         if (!StrUtil.equals(registMailCode,systemCode)) {
  72.             return ResponseEntity.failBusinessException(FAIL,"验证码错误!");
  73.         }
  74.         //3. 返回结果
  75.         return ResponseEntity.ok();
  76.     }
  77. }
复制代码
总结:

到这,我们已经学习了:

  • 邮箱协议的基本知识
  • 邮箱的发送和接收,在客户端和服务端的流程过程
  • 还知道了如何开通邮箱的第三方客户端使用服务
  • 还使用SpringBoot集成了邮箱,能够使用代码的形式,向邮箱发送邮件
以上内容是我个人根据网上的资料进行学习和转化,如果有错误,欢迎指出,共同进步哦~
(第一次写博客,写得很烂,哈哈哈见谅,希望你能够从垃圾中,灵活抽取出有用的东西吧)

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册