Erlo

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

2025-09-26 18:29:32 发布   65 浏览  
页面报错/反馈
收藏 点赞

先知

邮箱的一些基本概念:

发送邮件
  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协议

邮箱的传送流程

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

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

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

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

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

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

邮箱开通第三方服务

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

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

随便选择一个设备

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

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

找到账户中心

开启POP3、IMAP等服务

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

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

邮箱集成使用

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

  1. 依赖

    
    org.springframework.boot    
    spring-boot-starter-mail

  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  World!", true);

    // 发送
    javaMailSender.send(message);
}

类之间的关系

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

  1. JavaMailSender

  2. MimeMessage

  3. 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});
        }
    
    }
    
    ......
}

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

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

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

  3. 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邮件信息

区别在于:

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

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

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

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

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

邮箱验证码案例

  1. 依赖


    
    
        org.springframework.boot
        spring-boot-starter-web
    
    
        org.springframework.boot
        spring-boot-starter-test
    
    
        org.springframework.boot
        spring-boot-starter-mail
    
    
    
        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
    

  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

  1. 生成模板




    
    邮箱验证码


邮箱验证码

您好,

您正在进行邮箱验证,本次验证码如下(5分钟内有效):

88888

请在验证页面输入上方验证码完成验证。为保障账号安全,请勿将验证码告知他人。


如果这不是您本人的操作,请忽略此邮件。

  1. 生成自定义的JavaMailSender

因为邮箱验证码的场景,邮箱发送者一般是固定不变的,所以初始化为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 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集成了邮箱,能够使用代码的形式,向邮箱发送邮件

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

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

登录查看全部

参与评论

评论留言

还没有评论留言,赶紧来抢楼吧~~

手机查看

返回顶部

给这篇文章打个标签吧~

棒极了 糟糕透顶 好文章 PHP JAVA JS 小程序 Python SEO MySql 确认