springboot 发送邮件实战(完整代码)

springboot 发送邮件实战

前言:在我们实际项目中,有一些需求涉及到使用邮箱发送邮件的功能。Spring 框架提供了使用JavaMailSender接口发送电子邮件的抽象,Spring Boot 为其提供了自动配置以及启动模块。所以我们使用实现邮件发送功能还是相对来比较简单,spring官网邮件发送功能:https://docs.spring.io/spring-boot/docs/current/reference/html/io.html#io.email

一、准备工作

发送邮箱要开启邮件服务(以163邮箱为例)

进入对应的发送邮箱的设置

在这里插入图片描述

开启IMAP或者POP3任意一个服务都可以

在这里插入图片描述

至此,准备工作已经结束。

二、进入项目代码环节
2.1 项目引入邮箱的依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2.2 邮件的基本配置
# 属性名称:属性类型
    属性说明
# mail.smtp.user:String
    SMTP 的默认用户名。
# mail.smtp.host:String
    要连接的 SMTP 服务器。
# mail.smtp.port:int
    要连接的 SMTP 服务器端口,如果 connect() 方法没有明确指定一个。默认为 25。
# mail.smtp.connectiontimeout:int
    以毫秒为单位的套接字连接超时值。这个超时是由 java.net.Socket 实现的。默认为无限超时。
# mail.smtp.timeout:int
    以毫秒为单位的套接字读取超时值。这个超时是由 java.net.Socket 实现的。默认为无限超时。
# mail.smtp.writetimeout:int
    以毫秒为单位的套接字写入超时值。此超时是通过对每个连接使用 java.util.concurrent.ScheduledExecutorService 来实现的,
    该服务会在超时到期时安排线程关闭套接字。因此,使用此超时的开销是每个连接一个线程。默认为无限超时。
# mail.smtp.from:String
    用于 SMTP MAIL 命令的电子邮箱地址。这将设置信封返回地址。默认为 msg.getFrom() 或 InternetAddress.getLocalAddress()。
    注意:mail.smtp.user 以前用于此目的。
# mail.smtp.localhost:String
    SMTP HELO 或 EHLO 命令中使用的本地主机名。默认为InetAddress.getLocalHost().getHostName(). 
    如果您的 JDK 和名称服务配置正确,则通常不需要设置。
# mail.smtp.localaddress:String
    创建 SMTP 套接字时要绑定到的本地地址(主机名)。默认为 Socket 类选择的地址。通常不需要设置,
    但对于选择要绑定到的特定本地地址很重要的多宿主主机很有用。
# mail.smtp.localport:int
    创建 SMTP 套接字时要绑定到的本地端口号。默认为 Socket 类选择的端口号。
# mail.smtp.ehlo:boolean
    如果为 false,则不要尝试使用 EHLO 命令登录。默认为真。通常 EHLO 命令失败将回退到 HELO 命令;
    此属性仅适用于未正确使 EHLO 失败 或未正确实现 EHLO 的服务器。
# mail.smtp.auth:boolean
    如果为 true,则尝试使用 AUTH 命令对用户进行身份验证。默认为假。
# mail.smtp.auth.mechanisms:String
    如果设置,则列出要考虑的身份验证机制,以及考虑它们的顺序。只会使用服务器支持和当前实现支持的机制。
    默认为"LOGIN PLAIN DIGEST-MD5 NTLM",包括当前实现支持的所有认证机制,除了 XOAUTH2。
# mail.smtp.auth.login.disable:boolean
    如果为 true,则阻止使用该AUTH LOGIN命令。默认为假。
# mail.smtp.auth.plain.disable:boolean
    如果为 true,则阻止使用该AUTH PLAIN命令。默认为假。
# mail.smtp.auth.digest-md5.disable:boolean
    如果为 true,则阻止使用该AUTH DIGEST-MD5命令。默认为假。
# mail.smtp.auth.ntlm.disable:boolean
    如果为 true,则阻止使用该AUTH NTLM命令。默认为假。
# mail.smtp.auth.ntlm.domain:String
    NTLM 身份验证域。
# mail.smtp.auth.ntlm.flags:int
    NTLM 协议特定标志。有关详细信息,请参阅 http://curl.haxx.se/rfc/ntlm.html#theNtlmFlags。
# mail.smtp.auth.xoauth2.disable:boolean
    如果为 true,则阻止使用该AUTHENTICATE XOAUTH2命令。由于 OAuth 2.0 协议需要特殊的访问令牌而不是密码,因此默认情况下
    禁用此机制。通过将此属性显式设置为“false”或将“mail.smtp.auth.mechanisms”属性设置为“XOAUTH2”来启用它。
# mail.smtp.submitter:String
    要在 MAIL FROM 命令的 AUTH 标记中使用的提交者。通常由邮箱中继用于传递有关邮箱原始提交者的信息。
    另请参阅 的setSubmitter 方法SMTPMessage。邮箱客户端通常不使用它。
# mail.smtp.dsn.notify:String
    RCPT 命令的 NOTIFY 选项。NEVER 或 SUCCESS、FAILURE 和 DELAY(以逗号分隔)的某种组合。
# mail.smtp.dsn.ret:String
    MAIL 命令的 RET 选项。FULL 或 HDRS。
# mail.smtp.allow8bitmime:boolean
    如果设置为 true,并且服务器支持 8BITMIME 扩展,则使用“quoted-printable”或“base64”编码的邮箱的文本部分
    如果遵循 8bit 文本的 RFC2045 规则,则将转换为使用“8bit”编码。
# mail.smtp.sendpartial:boolean
    如果设置为 true,并且消息具有一些有效地址和一些无效地址,则无论如何都要发送消息,并使用 SendFailedException 报告部分失败。
    如果设置为 false(默认值),则如果收件人地址无效,则不会将邮箱发送给任何收件人。
# mail.smtp.sasl.enable:boolean
    如果设置为 true,则尝试使用 javax.security.sasl 包来选择登录的身份验证机制。默认为假。
# mail.smtp.sasl.mechanisms:String
    要尝试使用的 SASL 机制名称的空格或逗号分隔列表。
# mail.smtp.sasl.authorizationid:String
    在 SASL 身份验证中使用的授权 ID。如果未设置,则使用身份验证 ID(用户名)。
# mail.smtp.sasl.realm:String
    用于 DIGEST-MD5 身份验证的领域。
# mail.smtp.sasl.usecanonicalhostname:boolean
    如果设置为 true,则返回的规范主机名将 InetAddress.getCanonicalHostName 传递给 SASL 机制,
    而不是用于连接的主机名。默认为假。
# mail.smtp.quitwait:boolean
    如果设置为 false,则发送 QUIT 命令并立即关闭连接。如果设置为 true(默认值),则导致传输等待对 QUIT 命令的响应。
# mail.smtp.quitonsessionreject:boolean
    如果设置为 false(默认值),会话发起拒绝时不发送 QUIT 命令并立即关闭连接。
    如果设置为 true,则导致传输在关闭连接之前发送 QUIT 命令。
# mail.smtp.reportsuccess:boolean
    如果设置为 true,则会导致传输SMTPAddressSucceededException 为每个成功的地址包含一个 。还要注意 ,
    即使所有地址都正确并且消息已成功发送,这将导致SendFailedException 从sendMessage方法中 抛出 a SMTPTransport。
# mail.smtp.socketFactory:SocketFactory
    如果设置为实现该javax.net.SocketFactory接口的类,则 该类将用于创建 SMTP 套接字。请注意,这是一个类的实例,
    而不是名称,并且必须使用put方法而不是setProperty方法来设置 。
# mail.smtp.socketFactory.class:String
    如果设置,则指定实现javax.net.SocketFactory接口的类的名称 。此类将用于创建 SMTP 套接字。
# mail.smtp.socketFactory.fallback:boolean
    如果设置为 true,则无法使用指定的套接字工厂类创建套接字将导致使用java.net.Socket该类创建套接字。默认为真。
# mail.smtp.socketFactory.port:int
    指定使用指定套接字工厂时要连接的端口。如果未设置,将使用默认端口。
# mail.smtp.ssl.enable:boolean
    如果设置为 true,则默认使用 SSL 连接并使用 SSL 端口。“smtp”协议默认为 false,“smtps”协议默认为 true。
# mail.smtp.ssl.checkserveridentity:boolean
    如果设置为 true,请检查RFC 2595指定的服务器标识 。这些基于服务器证书内容的额外检查旨在防止中间人攻击。默认为假。
# mail.smtp.ssl.trust:String
    如果设置,并且未指定套接字工厂,则启用 MailSSLSocketFactory. 如果设置为“*”,则所有主机都是可信的。如果设置为以
    空格分隔的主机列表,则这些主机是可信的。否则,信任取决于服务器提供的证书。
# mail.smtp.ssl.socketFactory:SSLSocketFactory
    如果设置为扩展 javax.net.ssl.SSLSocketFactory类的类,则此类将用于创建 SMTP SSL 套接字。请注意,这是一个类的实例,
    而不是名称,并且必须使用put方法而不是setProperty方法来设置 。
# mail.smtp.ssl.socketFactory.class:String
    如果设置,则指定扩展javax.net.ssl.SSLSocketFactory类的类的名称 。此类将用于创建 SMTP SSL 套接字。
# mail.smtp.ssl.socketFactory.port:int
    指定使用指定套接字工厂时要连接的端口。如果未设置,将使用默认端口。
# mail.smtp.ssl.protocols:String
    指定将为 SSL 连接启用的 SSL 协议。属性值是该javax.net.ssl.SSLSocket.setEnabledProtocols方法
    可接受的以空格分隔的标记列表。
# mail.smtp.ssl.ciphersuites:String
    指定将为 SSL 连接启用的 SSL 密码套件。属性值是该javax.net.ssl.SSLSocket.setEnabledCipherSuites方法
    可接受的以空格分隔的标记列表。
# mail.smtp.starttls.enable:boolean
    如果为 true,则启用该STARTTLS命令(如果服务器支持)在发出任何登录命令之前将连接切换到受 TLS 保护的连接。
    如果服务器不支持 STARTTLS,则连接继续而不使用 TLS;mail.smtp.starttls.required 如果不支持 STARTTLS,
    请查看失败的 属性。请注意,必须配置适当的信任库,以便客户端信任服务器的证书。默认为假。
# mail.smtp.starttls.required:boolean
    如果为 true,则需要使用该STARTTLS命令。如果服务器不支持 STARTTLS 命令,或者命令失败,connect 方法就会失败。默认为假。
# mail.smtp.proxy.host:String
    指定将用于连接到邮箱服务器的 HTTP Web 代理服务器的主机名。
# mail.smtp.proxy.port:String
    指定 HTTP Web 代理服务器的端口号。默认为端口 80。
# mail.smtp.proxy.user:String
    指定用于向 HTTP Web 代理服务器进行身份验证的用户名。默认情况下,不进行身份验证。
# mail.smtp.proxy.password:String
    指定用于向 HTTP Web 代理服务器进行身份验证的密码。默认情况下,不进行身份验证。
# mail.smtp.socks.host:String
    指定将用于连接到邮箱服务器的 SOCKS5 代理服务器的主机名。
# mail.smtp.socks.port:String
    指定 SOCKS5 代理服务器的端口号。仅当代理服务器未使用标准端口号 1080 时才需要使用此选项。
# mail.smtp.mailextension:String
    附加到 MAIL 命令的扩展字符串。扩展字符串可用于指定标准 SMTP 服务扩展以及特定于供应商的扩展。通常,
    应用程序应使用该 SMTPTransport 方法supportsExtension 来验证服务器是否支持所需的服务扩展。
    请参阅RFC 1869 和其他定义特定扩展的 RFC。
# mail.smtp.userset:boolean
    如果设置为true,则在isConnected方法中使用RSET 命令而不是NOOP 命令。
    在某些情况下,sendmail 在多次 NOOP 命令后响应会很慢;使用 RSET 避免了这个 sendmail 问题。默认为假。
# mail.smtp.noop.strict:boolean
    如果设置为 true(默认值),则坚持来自 NOOP 命令的 250 响应代码以指示成功。该isConnected方法使用 NOOP 命令 来确定
    连接是否仍然有效。一些较旧的服务器在成功时返回错误的响应代码,一些服务器根本不执行 NOOP 命令,因此总是返回失败代码。
    将此属性设置为 false 以处理以这种方式损坏的服务器。通常,当服务器超时连接时,它会发送 421 响应代码,客户端将其视
    为对其发出的下一个命令的响应。某些服务器在连接超时时发送错误的失败响应代码。在处理以这种方式损坏的服务器时,
    不要将此属性设置为 false。

上述是邮箱properties 扩展配置,下面是基本配置

spring:
  mail:
    host: smtp.163.com #smtp服务器主机(163的)
    port: 25 # 连接邮件服务器端口(默认SMTP 25 POP 110)
    protocol: smtp # 连接协议(默认SMTP)
    username: xxxxx@163.com  # 登录服务器邮箱账号
    password: xxxxxxxxxxxx # 登录服务器邮箱授权码(不是邮箱密码,这个是我们开通SMTP、POP时得到的授权码)
    default-encoding: UTF-8 # 编码
    test-connection: false # 是否测试连接
    properties:
      mail:
        smtp:
          from: xxxxx@163.com #默认发送方邮箱账号(当程序未指定发件人邮箱则默认取这个)
          auth: true # 开启权限认证
          timeout: 25000 # 邮件接收时间的限制
          connectiontimeout: 25000 # 连接时间的限制
          writetimeout: 25000 # 邮件发送时间的限制(毫秒)
        debug: true # 日志打印,邮件发送过程的日志会被输出
2.2 实现发送邮件的功能

JavaMailSenderJavaMailSenderImpl;它们是Spring官方提供的一套邮件功能集成接口及实现。我们在业务里直接注入JavaMailSenderImpl后并调用send方法。其中简单的邮件我们可以通过SimpleMailMessage来发送,对于复杂的带有附件的我们可以借助MimeMessageHelper来构建MimeMessage发送邮件。

发送邮件有三种常见的发送方式:

1、发送简单的文本内容

2、发送复杂的邮件HTML+图片资源+附件

3、发送复杂邮件使用Thymeleaf模板

2.2.1 发送简单的文本内容
	@Autowired
    private JavaMailSender javaMailSender;

    @Override
    public void sendSimpleMail(String from, String to, String subject, String text) {
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        // 发件人
        simpleMailMessage.setFrom(from);
        // 收件人
        simpleMailMessage.setTo(to);
        // 邮件主题
        simpleMailMessage.setSubject(subject);
        // 邮件内容
        simpleMailMessage.setText(text);
        javaMailSender.send(simpleMailMessage);
    }
2.2.2 发送带附件的邮件
	@Override
    public Boolean sendMimeMail(String from, String to, String subject, String text) {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        String url = "https://www.w3school.com.cn/example/xmle/note.xml";
        String filePath = "D:\\workFile";
        try {
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(text);

            String fileName = XFileUtils.downLoadByUrl(url, filePath, null);
            String fullFilePath = filePath + File.separator + fileName;
            File file = new File(fullFilePath);
            if (!file.exists()) {
                return false;
            }
            FileSystemResource fileSystemResource = new FileSystemResource(file);
            helper.addAttachment(fileName, fileSystemResource);
            javaMailSender.send(mimeMessage);
            file.delete();
        } catch (Exception e) {
            log.error("发送复杂邮箱失败",e);
            return false;
        }
        return true;
    }
2.2.3 发送使用模板的邮件

a. 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

b. resource/templates文件夹中添加一个名为template.html的文件

<!DOCTYPE html>
<html lang="en">
<head>
    <title>邮箱验证</title>
    <meta charset="utf-8">
</head>

<body>
<!-- 头部 -->
<div style="padding: 10px; background-color: #393D49;">
    <h2 style="color: #FFFFFF; margin: 0px;"></h2>
</div>
<!-- 内容 -->
<div style="padding-top: 10px; padding-bottom: 10px;">
    <div style="background-color: snow; padding: 20px;">
        <div>
            <h3>尊敬的用户:您好!</h3>
            <p>说明:您现在正在进行敏感操作,为了确保您的账户安全,我们将通过邮件对您进行身份验证。</p>
            <p th:text="${message}"></p>
            <div>
                <h4>本次的验证码为:</h4>
                <div style="background-color: #EBEEF5; padding: 10px;">
                    <h3 th:text="${code}"></h3>
                </div>
                <h4>有效期为5分钟</h4>
            </div>
        </div>
    </div>
</div>
<!-- 页底 -->
<div style="padding: 10px; text-align: center; background-color: #2F4056;">
    <p style="margin: 0px; color: #FFFFFF;"></p>
</div>

</body>
</html>

c. 发送邮件代码

	@RequestMapping("/sendTemplateMail")
    public Boolean sendTemplateMail(){
        String from = "XXXXX@163.com";
        String to = "XXXXX@163.com";
        String subject = "这是模板邮件的标题";
        String message = "详情:您正在尝试进行登录操作,若非是您本人的行为,请忽略!";
        String code = "123456789";
        Context context = new Context();
        context.setVariable("message", message);
        context.setVariable("code", code);
        return emailService.sendTemplateMail(from, to, subject, context );
    }
 	@Override
    public Boolean sendTemplateMail(String from, String to, String subject, Context context) {

        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
            String mail = templateEngine.process("template.html", context);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(mail,true);
            javaMailSender.send(mimeMessage);
        } catch (Exception e) {
            log.error("发送模板邮箱失败",e);
            return false;
        }
        return true;
    }