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

先知

邮箱的一些基本概念:

发送邮件
  1. STMP协议:

    1. Simple Mail Transfer Protocol ,简单邮箱传输协议,用于发送邮件的协议。

    2. 基于TCP,保证可靠性,但不安全,是明文传输

    3. Spring Boot默认也是基于此协议进行发送邮件

接收邮件
  1. POP3协议:

    1. Post Office Protocol 3:邮局通信协议第三版,用于接收邮箱的标准协议。

    2. 一次性取信”,客户端把邮件下载到本地之后,通常会从服务器上删除(可以配置保留副本)。如果没有设置,电脑上收了邮件,手机上就没法再看到这封邮件

  2. IMAP协议:

    1. Internet Message Access Protocol : 互联网消息访问协议,是POP3的代替,也是用于接收邮件的协议

    2. 云端管理”:客户端拉去邮件,只是将“视图”同步到本地,不会删除,始终保存在服务器上。

    3. Gmail、QQ邮箱、OutLook等现代邮箱,都是用的IMAP协议

邮箱的传送流程

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

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

  1. QQ邮箱客户端会使用SMTP协议将 邮件 发送到自家的邮件服务器上

  2. QQ邮箱服务器,接收到一封邮件,然后会解析目标地址的域名

  3. QQ邮箱服务器,识别到是其他服务器上的域名,就会进行转发,同样使用SMTP协议发送

  4. 网易邮箱服务器接收到一封邮件,发现是目标地址是自己的域名,就将信封存储在服务器上

  5. 网易邮箱客户器上线,查看服务器上有没有邮件,如果有,就拉取。使用IMAP或者POP3协议

邮箱开通第三方服务

  1. 如果你使用的是飞书邮箱

找到邮箱的位置,“第三方邮箱客户端登陆”

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

随便选择一个设备

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

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

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

  1. 如果你使用的是QQ邮箱

找到账户中心

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

开启POP3、IMAP等服务

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

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

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

  1. 其他的网易邮箱等,获取授权码的方式类似

邮箱集成使用

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

  1. 依赖

<dependency>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-mail</artifactId> </dependency> 
  1. 配置文件

spring:   mail:   # 指定邮箱的服务器地址     host: smtp.feishu.cn     # 执行邮箱的发送者,由哪一个邮箱账号来发送邮件     username: huag****@ifree8.com     # 邮件的授权码,邮箱账号需要开通第三方服务     password: 29F7s********     default-encoding: UTF-8    
  1. 测试

@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); } 

类之间的关系

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

  1. JavaMailSender

  2. MimeMessage

  3. MimeMessageHelper

JavaMailSender

类的继承关系

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

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

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

  • 底层实现自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});         }          }          ...... } 

来看看里面都有哪些方法和属性

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

  1. setFrom():设置邮箱展示的信息,比如名称和邮箱等

  2. setRecipients():设置收件人的邮箱

  3. setText():邮件内容

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

MimeMessageHelper

Helper,“包装快递”的好帮手

看看Helper中提供了些什么方法?

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

为什么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邮件信息

区别在于:

  1. MimeMessage底层的****API,能够做所有的事情

  2. MimeMessage适用的参数大多是已经封装好的,比如setFrom()方法中的参数是Address,这个类包装了邮件的地址等很多信息。

  3. MimeMessageHelper是提供的工具类,旨在于帮助用户更方便的设置Message的属性

  4. MimeMessageHelper在设置复杂邮件内容(HTML 模板邮件 + 附件),会帮助用户省去很多封装的步骤,底层进行处理,简化代码。比如setFrom()方法中的参数是String,数据更加的原始。

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

邮箱验证码案例

  1. 依赖

<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> 
  1. 使用配置类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 

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

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

  1. 生成模板

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

<!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> 
  1. 生成自定义的JavaMailSender

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

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

  1. 使用

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();     } } 

总结:

到这,我们已经学习了:

  1. 邮箱协议的基本知识

  2. 邮箱的发送和接收,在客户端和服务端的流程过程

  3. 还知道了如何开通邮箱的第三方客户端使用服务

  4. 还使用SpringBoot集成了邮箱,能够使用代码的形式,向邮箱发送邮件

以上内容是我个人根据网上的资料进行学习和转化,如果有错误,欢迎指出,共同进步哦~

(第一次写博客,写得很烂,哈哈哈见谅,希望你能够从垃圾中,灵活抽取出有用的东西吧)

发表评论

评论已关闭。

相关文章