一:简述
在日常中的工作中难免会遇到程序集成邮件发送功能、接收功能;此篇文章我将使用SpringBoot集成邮件发送功能和接收功能;若对邮件一些基本协议和发送流程不懂的请务必参考我之前写的博客或者浏览网上资料。
【邮件基本概念及发送方式】 【JavaMail发送邮件(超详细)】
二:SpringBoot发送邮件的基本说明
在我们现在使用的SpringBoot的版本中,底层发送邮件的技术都是使用一个叫 Jakarta Mail 的,它可有实现SMTP、POP、IMAP等基本的邮件发送和接收协议,因为前两篇我都介绍了,这里就不在多说,直接上干货!!
1:基本环境及坐标依赖
使用SpringBoot脚手架创建一个SpringBoot项目后导入下面邮件发送坐标,或者在脚手架创建中也可有选择
<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-mailartifactId> dependency>
2:快速上手发送邮件
在发送邮件前我简单说说JavaMailSender和JavaMailSenderImpl;它们是Spring官方提供的一套邮件功能集成接口及实现,底层调用具体的Jakarta Mail技术;可以说是二次封装;使用SpringBoot提供的接口及实现是后端邮件发送最主流的集成工具;我们在业务里直接注入JavaMailSenderImpl后并调用send方法。其中简单的邮件我们可以通过SimpleMailMessage来发送,对于复杂的带有附件的我们可以借助MimeMessageHelper来构建MimeMessage发送邮件。
application.properties设置基本配置 ### smtp服务器主机(163的) spring.mail.host=smtp.163.com ### 登录服务器邮箱账号 spring.mail.username=antladdie ### 登录服务器邮箱授权码(不是邮箱密码,这个是我们开通SMTP、POP时得到的授权码) spring.mail.password=xxxxxxxxxxxxxxx
@SpringBootTest class SpringbootJakartamailApplicationTests { //注入邮件发送对象 @Autowired private JavaMailSender mailSender; /*** * 简单邮件发送 */ @Test void testMailA() { //创建简单的邮件发送对象 SimpleMailMessage message = new SimpleMailMessage(); message.setFrom("[email protected]"); // 设置发件人邮箱(若配置默认邮箱则不用再设置) message.setTo("[email protected]"); // 设置收件人邮箱 message.setCc("[email protected]"); // 设置抄报人邮箱(可以不填写) message.setBcc("[email protected]"); // 设置密送人邮箱(可以不填写) message.setSubject("缴费通知"); // 设置邮件主题 message.setText("您手机已欠费100元,请及时缴费!"); // 设置邮件文本内容 message.setSentDate(new Date()); // 设置邮件发送时间 //发送 mailSender.send(message); } }
3:集成邮件的基本配置(约定大于配置)
这里的 Jakarta Mail 的 properties 扩展配置不理解的可以参考 官方文档 ; 不过英文的看着头大,这里我将其翻译了
# 属性名称:属性类型 属性说明 # 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。 除了打印由Session配置控制的调试输出之外,com.sun.mail.smtp 提供程序使用Logger下表中的描述记录相同的信息 : 记录器名称 日志级别 目的 com.sun.mail.smtp CONFIG SMTPTransport 的配置 com.sun.mail.smtp FINE 一般调试输出 com.sun.mail.smtp.protocol FINEST 完整的协议跟踪 警告:此包独有的 API 应视为实验性的。将来它们可能会以与使用当前 API 的应用程序不兼容的方式进行更改
#application.properties基本配置,后面我都使用此配置来发送邮件 ## 基本配置 ### smtp服务器主机(163的) spring.mail.host=smtp.163.com ### 连接邮件服务器端口(默认SMTP 25 POP 110) spring.mail.port=25 ### 服务协议SMTP(代表是发送邮件) spring.mail.protocol=smtp ### 登录服务器邮箱账号 spring.mail.username=antladdie ### 登录服务器邮箱授权码(不是邮箱密码,这个是我们开通SMTP、POP时得到的授权码) spring.mail.password=xxxxxxxxxxxxx ### 默认邮件的编码集(MimeMessage 编码,默认UTF-8) spring.mail.default-encoding=UTF-8 # 补充配置(这里具体可以参照Jakarta Mail的扩展配置) ## 默认发送方邮箱账号(当程序未指定发件人邮箱则默认取这个) spring.mail.properties.mail.smtp.from=antladdie@163.com ## 开启权限认证 spring.mail.properties.mail.smtp.auth=true ## 邮件接收时间的限制 spring.mail.properties.mail.smtp.timeout=60000 ## 连接时间的限制 spring.mail.properties.mail.smtp.connectiontimeout=60000 ## 邮件发送时间的限制(毫秒) spring.mail.properties.mail.smtp.writetimeout=60000 ## 日志打印,邮件发送过程的日志会被输出 spring.mail.properties.mail.debug=true
三:复杂邮件发送 HTML+图片资源+附件
@SpringBootTest class SpringbootJakartamailApplicationTests { //注入邮件发送对象 @Autowired private JavaMailSender mailSender; @Test void testMailB() throws MessagingException { //创建复杂有限发送对象 MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true); messageHelper.setFrom("[email protected]"); // 设置发件人邮箱(若配置默认邮箱则不用再设置) messageHelper.setTo("[email protected]"); // 设置收件人邮箱 messageHelper.setCc("[email protected]"); // 设置抄报人邮箱(可以不填写) messageHelper.setBcc("[email protected]"); // 设置密送人邮箱(可以不填写) messageHelper.setSubject("缴费通知"); // 设置邮件主题 //获取项目资源根目录 resources/file 并准备资源 String rootPath = Objects.requireNonNull(SpringbootJakartamailApplicationTests.class.getClassLoader().getResource("file")).getFile(); FileSystemResource png = new FileSystemResource(new File(rootPath + "/ab.png")); FileSystemResource xls = new FileSystemResource(new File(rootPath + "/student.xls")); FileSystemResource mp3 = new FileSystemResource(new File(rootPath + "/mu.mp3")); FileSystemResource zip = new FileSystemResource(new File(rootPath + "/redis.zip")); //关于附件 资源 HTML 文本的设置 //设置附件 //设置一个 图片附件 messageHelper.addAttachment(Objects.requireNonNull(png.getFilename()), png); //设置一个 excel附件 messageHelper.addAttachment(Objects.requireNonNull(xls.getFilename()), xls); //设置一个 mp3附件 messageHelper.addAttachment(Objects.requireNonNull(mp3.getFilename()), mp3); //设置一个 zip附件 不过发送垃圾附件可能会被识别 554 HL:IHU 发信IP因发送垃圾邮件或存在异常的连接行为 messageHelper.addAttachment(Objects.requireNonNull(zip.getFilename()), zip); //设置邮件内容 cid:资源id 在内容中引用资源 后面true代表是html内容 messageHelper.setText("欠费通知:您已欠费200元
", true); //设置资源 FileSystemResource resPng = new FileSystemResource(new File(rootPath + "/b.png")); messageHelper.addInline("p01",resPng); //发送 mailSender.send(mimeMessage); } }
四:复杂邮件发送使用Thymeleaf模板
使用模板和不使用模板没太大区别,只是使用模板则将xx.html文件渲染成String类型的字符串文本再引用
<dependency> <groupId>org.thymeleafgroupId> <artifactId>thymeleafartifactId> <version>3.0.12.RELEASEversion> dependency>
doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no,
initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
head>
<body>
<h2 style='color:#f00;'>
<span th:text="${message}">span>
<img src='cid:p01' alt='' style='width:200px;height:50px;'>
h2>
body>
html>
/*** * 模板解析方法,解析出一个String的html返回 * @return */ public String templateHtml(){ //设置类加载模板处理器 ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver(); //设置前缀后缀 resolver.setPrefix("/file/"); resolver.setSuffix(".html"); //创建模板引擎处理器 TemplateEngine engine = new TemplateEngine(); //设置引擎使用的模板文件 engine.setTemplateResolver(resolver); //创建Context来为模板设置填充数据 Context context = new Context(); //填充模板里的数据 context.setVariable("message","欠费通知:你已经欠费100元"); //具体处理,把模板和数据合并成一个新的文本 //注:文件我直接放在resources/templates文件根目录下,如果有多层目录,需要写明文件位置(或者设置过前缀和后缀) return engine.process("emailTemplate", context); }
5:结尾
讲到这,已经对SpringBoot集成邮件发送功能说完了,若想知道如何接收并解析邮件请参考我上面给出的博客,使用javaMail发送和接收邮件;不过话说回来在大部分公司里使用SMTP、POP3协议发送和接收是完全够用的,不过我上次在项目组负责邮箱的发送使用的是内网的企业邮箱,走的是Exchange协议,这个就和我们之前讲的不一样的,下一篇我将带大家看看如何使用Exchange方法发送邮件。