Spring Boot 发送邮件,端口号大有玄机

2023-11-08 13:20

本文主要是介绍Spring Boot 发送邮件,端口号大有玄机,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1. 什么是 SMTP
    • 2. SMTP Port
      • 2.1 25
      • 2.2 587
      • 2.3 465
      • 2.4 小结
    • 3. 号外
      • 3.1 邮件基础
      • 3.2 准备工作
      • 3.3 项目创建
        • 3.3.1 发送简单邮件
        • 3.3.2 发送带附件的邮件
        • 3.3.3 发送带图片资源的邮件
        • 3.3.4 使用 Freemarker 作邮件模板
        • 3.3.5 使用 Thymeleaf 作邮件模板

Spring Boot 发送邮件,松哥之前专门写过文章,这里就不啰嗦了。还不懂 Spring Boot 如何发送邮件的小伙伴,翻到本文后半部分,有介绍。

今天想和大家聊一下 SMTP 服务器的端口问题,这个也是一个小伙伴提的问题,SMTP 服务器有众多端口:25、465、587 各自间有什么区别?可以随意使用吗?希望今天这篇文章能给你答案。

1. 什么是 SMTP

SMTP 代表简单邮件传输协议,简而言之,它是通过 Internet 发送电子邮件的过程。计算机端口是个人计算机连接到网络并完成数据传输的方式。SMTP 端口是两者的组合:设计用于通过网络向其收件人发送电子邮件的端口。

下图展示了 SMTP 协议在邮件发送过程的作用:

当然,就像有多个计算机端口一样,可以使用的 SMTP 端口也有很多。

2. SMTP Port

2.1 25

1982 年,南加州大学向 Internet 工程任务组 (IETF) 提交了一份提案,即 Request For Comments (RFC) 821,将端口 25 建立为 Internet 电子邮件的默认传输通道。40 年过去了,如今我们依然可以使用 25 这个端口在两个邮件服务器之间传输邮件。

不过最初的设计没有考虑安全问题,在 1998 年 12 月,R. Gellens 和 J. Klensin 提交了 RFC2476,在这个规范中,RFC 提议将传统的消息提交和消息中继概念分开,RFC 定义消息提交应通过端口 587 进行(即我们通过邮件客户端等工具提交邮件的时候,应该使用 587 端口),以确保新的策略和安全要求不会干扰消息中继端口 25 上的传统中继流量。

这么一拆分,端口 25 就主要用于 SMTP 中继,也就是将邮件从一个电子邮件服务器传输到另一个电子邮件服务器。

在大多数情况下,SMTP 电子邮件客户端(Foxmail、Microsoft Outlook、Mail、Thunderbird 等)不应使用 25 端口,以遏制垃圾邮件的数量,所以这个 25 端口和我们个人使用的关系就不大。

2.2 587

这是默认的邮件提交端口,当用户提交一封电子邮件到邮件服务器时,可以使用该端口,我们自己通过 Java 代码发送邮件,也可以使用该端口。

端口 587 与 TLS 加密相结合,可确保安全提交电子邮件并遵循 IETF 制定的指导方针。

2.3 465

那按理说我们发送邮件的时候就该使用 587 端口呀,465 又是干嘛的?

IETF 从未将端口 465 发布为官方 SMTP 传输或提交端口,然而维护大部分核心互联网基础设施的 IANA 为 SMTPS 分配了端口 465。目的是为 SMTP 建立一个端口,以便使用安全套接字层 (SSL) 进行操作,这样使得邮件发送更加安全。

所以 465 和 587 其实都是为了邮件安全,但是两者的思路不一样,465 是 SSL,587 则是 TLS,SSL 和 TLS 有啥区别呢?这个就说来话长了,简单一句话就是:TLS(传输层安全)是更为安全的升级版 SSL,TLS 是 SSL 标准化后的产物。

按理说 465 应该被撤销,大家都用 587,但是由于 465 曾经被 IANA 认定为有效,因此可能存在仅能够使用此端口连接的遗留系统,所以该端口并没有被废弃,也可以使用。

2.4 小结

好啦,这就是这几个端口的区别。一般来说,我们用 Spring Boot 发送邮件的时候,465 和 587 都能用,但是不建议使用 25。另外在使用 465 或者 587 的时候,有的个别邮箱如 139 邮箱需要配置如下属性:

spring.mail.properties.mail.smtp.ssl.enable=true

3. 号外

可能还有小伙伴不懂 Spring Boot 邮件发送,再来回顾下。

邮件发送其实是一个非常常见的需求,用户注册,找回密码等地方,都会用到,使用 JavaSE 代码发送邮件,步骤还是挺繁琐的,Spring Boot 中对于邮件发送,提供了相关的自动化配置类,使得邮件发送变得非常容易,接下来我们就来一探究竟!看看使用 Spring Boot 发送邮件的 5 中姿势。

3.1 邮件基础

我们经常会听到各种各样的邮件协议,比如 SMTP、POP3、IMAP ,那么这些协议有什么作用,有什么区别?我们先来讨论一下这个问题。

SMTP 是一个基于 TCP/IP 的应用层协议,江湖地位有点类似于 HTTP,SMTP 服务器默认监听的端口号为 25 。看到这里,小伙伴们可能会想到既然 SMTP 协议是基于 TCP/IP 的应用层协议,那么我是不是也可以通过 Socket 发送一封邮件呢?回答是肯定的。

生活中我们投递一封邮件要经过如下几个步骤:

  1. 深圳的小王先将邮件投递到深圳的邮局
  2. 深圳的邮局将邮件运送到上海的邮局
  3. 上海的小张来邮局取邮件

这是一个缩减版的生活中邮件发送过程。这三个步骤可以分别对应我们的邮件发送过程,假设从 aaa@qq.com 发送邮件到 111@163.com :

  1. aaa@qq.com 先将邮件投递到腾讯的邮件服务器
  2. 腾讯的邮件服务器将我们的邮件投递到网易的邮件服务器
  3. 111@163.com 登录网易的邮件服务器查看邮件

邮件投递大致就是这个过程,这个过程就涉及到了多个协议,我们来分别看一下。

SMTP 协议全称为 Simple Mail Transfer Protocol,译作简单邮件传输协议,它定义了邮件客户端软件与 SMTP 服务器之间,以及 SMTP 服务器与 SMTP 服务器之间的通信规则。

也就是说 aaa@qq.com 用户先将邮件投递到腾讯的 SMTP 服务器这个过程就使用了 SMTP 协议,然后腾讯的 SMTP 服务器将邮件投递到网易的 SMTP 服务器这个过程也依然使用了 SMTP 协议,SMTP 服务器就是用来收邮件。

而 POP3 协议全称为 Post Office Protocol ,译作邮局协议,它定义了邮件客户端与 POP3 服务器之间的通信规则,那么该协议在什么场景下会用到呢?当邮件到达网易的 SMTP 服务器之后, 111@163.com 用户需要登录服务器查看邮件,这个时候就该协议就用上了:邮件服务商都会为每一个用户提供专门的邮件存储空间,SMTP 服务器收到邮件之后,就将邮件保存到相应用户的邮件存储空间中,如果用户要读取邮件,就需要通过邮件服务商的 POP3 邮件服务器来完成。

最后,可能也有小伙伴们听说过 IMAP 协议,这个协议是对 POP3 协议的扩展,功能更强,作用类似,这里不再赘述。

3.2 准备工作

目前国内大部分的邮件服务商都不允许直接使用用户名/密码的方式来在代码中发送邮件,都是要先申请授权码,这里以 QQ 邮箱为例,向大家演示授权码的申请流程:首先我们需要先登录 QQ 邮箱网页版,点击上方的设置按钮:

然后点击账户选项卡:

在账户选项卡中找到开启POP3/SMTP选项,如下:

点击开启,开启相关功能,开启过程需要手机号码验证,按照步骤操作即可,不赘述。开启成功之后,即可获取一个授权码,将该号码保存好,一会使用。

3.3 项目创建

接下来,我们就可以创建项目了,Spring Boot 中,对于邮件发送提供了自动配置类,开发者只需要加入相关依赖,然后配置一下邮箱的基本信息,就可以发送邮件了。

  • 首先创建一个 Spring Boot 项目,引入邮件发送依赖:

创建完成后,项目依赖如下:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 配置邮箱基本信息

项目创建成功后,接下来在 application.properties 中配置邮箱的基本信息:

spring.mail.host=smtp.qq.com
spring.mail.port=587
spring.mail.username=1510161612@qq.com
spring.mail.password=ubknfzhjkhrbbabe
spring.mail.default-encoding=UTF-8
spring.mail.properties.mail.smtp.socketFactoryClass=javax.net.ssl.SSLSocketFactory
spring.mail.properties.mail.debug=true

配置含义分别如下:

  • 配置 SMTP 服务器地址
  • SMTP 服务器的端口
  • 配置邮箱用户名
  • 配置密码,注意,不是真正的密码,而是刚刚申请到的授权码
  • 默认的邮件编码
  • 配饰 SSL 加密工厂
  • 表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误

如果不知道 smtp 服务器的端口或者地址的的话,可以参考 腾讯的邮箱文档

  • https://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=371

做完这些之后,Spring Boot 就会自动帮我们配置好邮件发送类,相关的配置在 org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration 类中,部分源码如下:

@Configuration
@ConditionalOnClass({ MimeMessage.class, MimeType.class, MailSender.class })
@ConditionalOnMissingBean(MailSender.class)
@Conditional(MailSenderCondition.class)
@EnableConfigurationProperties(MailProperties.class)
@Import({ MailSenderJndiConfiguration.class, MailSenderPropertiesConfiguration.class })
public class MailSenderAutoConfiguration {
}

从这段代码中,可以看到,导入了另外一个配置 MailSenderPropertiesConfiguration 类,这个类中,提供了邮件发送相关的工具类:

@Configuration
@ConditionalOnProperty(prefix = "spring.mail", name = "host")
class MailSenderPropertiesConfiguration {private final MailProperties properties;MailSenderPropertiesConfiguration(MailProperties properties) {this.properties = properties;}@Bean@ConditionalOnMissingBeanpublic JavaMailSenderImpl mailSender() {JavaMailSenderImpl sender = new JavaMailSenderImpl();applyProperties(sender);return sender;}
}

可以看到,这里创建了一个 JavaMailSenderImpl 的实例, JavaMailSenderImplJavaMailSender 的一个实现,我们将使用 JavaMailSenderImpl 来完成邮件的发送工作。

做完如上两步,邮件发送的准备工作就算是完成了,接下来就可以直接发送邮件了。

具体的发送,有 5 种不同的方式,我们一个一个来看。

3.3.1 发送简单邮件

简单邮件就是指邮件内容是一个普通的文本文档:

@Autowired
JavaMailSender javaMailSender;
@Test
public void sendSimpleMail() {SimpleMailMessage message = new SimpleMailMessage();message.setSubject("这是一封测试邮件");message.setFrom("1510161612@qq.com");message.setTo("25xxxxx755@qq.com");message.setCc("37xxxxx37@qq.com");message.setBcc("14xxxxx098@qq.com");message.setSentDate(new Date());message.setText("这是测试邮件的正文");javaMailSender.send(message);
}

从上往下,代码含义分别如下:

  1. 构建一个邮件对象
  2. 设置邮件主题
  3. 设置邮件发送者
  4. 设置邮件接收者,可以有多个接收者
  5. 设置邮件抄送人,可以有多个抄送人
  6. 设置隐秘抄送人,可以有多个
  7. 设置邮件发送日期
  8. 设置邮件的正文
  9. 发送邮件

最后执行该方法,就可以实现邮件的发送,发送效果图如下:

3.3.2 发送带附件的邮件

邮件的附件可以是图片,也可以是普通文件,都是支持的。

@Test
public void sendAttachFileMail() throws MessagingException {MimeMessage mimeMessage = javaMailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);helper.setSubject("这是一封测试邮件");helper.setFrom("1510161612@qq.com");helper.setTo("25xxxxx755@qq.com");helper.setCc("37xxxxx37@qq.com");helper.setBcc("14xxxxx098@qq.com");helper.setSentDate(new Date());helper.setText("这是测试邮件的正文");helper.addAttachment("javaboy.jpg",new File("C:\\Users\\sang\\Downloads\\javaboy.png"));javaMailSender.send(mimeMessage);
}

注意这里在构建邮件对象上和前文有所差异,这里是通过 javaMailSender 来获取一个复杂邮件对象,然后再利用 MimeMessageHelper 对邮件进行配置,MimeMessageHelper 是一个邮件配置的辅助工具类,创建时候的 true 表示构建一个 multipart message 类型的邮件,有了 MimeMessageHelper 之后,我们针对邮件的配置都是由 MimeMessageHelper 来代劳。

最后通过 addAttachment 方法来添加一个附件。

执行该方法,邮件发送效果图如下:

3.3.3 发送带图片资源的邮件

图片资源和附件有什么区别呢?图片资源是放在邮件正文中的,即一打开邮件,就能看到图片。但是一般来说,不建议使用这种方式,一些公司会对邮件内容的大小有限制(因为这种方式是将图片一起发送的)。

@Test
public void sendImgResMail() throws MessagingException {MimeMessage mimeMessage = javaMailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setSubject("这是一封测试邮件");helper.setFrom("1510161612@qq.com");helper.setTo("25xxxxx755@qq.com");helper.setCc("37xxxxx37@qq.com");helper.setBcc("14xxxxx098@qq.com");helper.setSentDate(new Date());helper.setText("<p>hello 大家好,这是一封测试邮件,这封邮件包含两种图片,分别如下</p><p>第一张图片:</p><img src='cid:p01'/><p>第二张图片:</p><img src='cid:p02'/>",true);helper.addInline("p01",new FileSystemResource(new File("C:\\Users\\sang\\Downloads\\javaboy.png")));helper.addInline("p02",new FileSystemResource(new File("C:\\Users\\sang\\Downloads\\javaboy2.png")));javaMailSender.send(mimeMessage);
}

这里的邮件 text 是一个 HTML 文本,里边涉及到的图片资源先用一个占位符占着,setText 方法的第二个参数 true 表示第一个参数是一个 HTML 文本。

setText 之后,再通过 addInline 方法来添加图片资源。

最后执行该方法,发送邮件,效果如下:

在公司实际开发中,第一种和第三种都不是使用最多的邮件发送方案。因为正常来说,邮件的内容都是比较的丰富的,所以大部分邮件都是通过 HTML 来呈现的,如果直接拼接 HTML 字符串,这样以后不好维护,为了解决这个问题,一般邮件发送,都会有相应的邮件模板。最具代表性的两个模板就是 Freemarker 模板和 Thyemeleaf 模板了。

3.3.4 使用 Freemarker 作邮件模板

首先需要引入 Freemarker 依赖:

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

然后在 resources/templates 目录下创建一个 mail.ftl 作为邮件发送模板:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<p>hello 欢迎加入 xxx 大家庭,您的入职信息如下:</p>
<table border="1"><tr><td>姓名</td><td>${username}</td></tr><tr><td>工号</td><td>${num}</td></tr><tr><td>薪水</td><td>${salary}</td></tr>
</table>
<div style="color: #ff1a0e">一起努力创造辉煌</div>
</body>
</html>

接下来,将邮件模板渲染成 HTML ,然后发送即可。

@Test
public void sendFreemarkerMail() throws MessagingException, IOException, TemplateException {MimeMessage mimeMessage = javaMailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setSubject("这是一封测试邮件");helper.setFrom("1510161612@qq.com");helper.setTo("25xxxxx755@qq.com");helper.setCc("37xxxxx37@qq.com");helper.setBcc("14xxxxx098@qq.com");helper.setSentDate(new Date());//构建 Freemarker 的基本配置Configuration configuration = new Configuration(Configuration.VERSION_2_3_0);// 配置模板位置ClassLoader loader = MailApplication.class.getClassLoader();configuration.setClassLoaderForTemplateLoading(loader, "templates");//加载模板Template template = configuration.getTemplate("mail.ftl");User user = new User();user.setUsername("javaboy");user.setNum(1);user.setSalary((double) 99999);StringWriter out = new StringWriter();//模板渲染,渲染的结果将被保存到 out 中 ,将out 中的 html 字符串发送即可template.process(user, out);helper.setText(out.toString(),true);javaMailSender.send(mimeMessage);
}

需要注意的是,虽然引入了 Freemarker 的自动化配置,但是我们在这里是直接 new Configuration 来重新配置 Freemarker 的,所以 Freemarker 默认的配置这里不生效,因此,在填写模板位置时,值为 templates

调用该方法,发送邮件,效果图如下:

3.3.5 使用 Thymeleaf 作邮件模板

推荐在 Spring Boot 中使用 Thymeleaf 来构建邮件模板。因为 Thymeleaf 的自动化配置提供了一个 TemplateEngine,通过 TemplateEngine 可以方便的将 Thymeleaf 模板渲染为 HTML ,同时,Thymeleaf 的自动化配置在这里是继续有效的 。

首先,引入 Thymeleaf 依赖:

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

然后,创建 Thymeleaf 邮件模板:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<p>hello 欢迎加入 xxx 大家庭,您的入职信息如下:</p>
<table border="1"><tr><td>姓名</td><td th:text="${username}"></td></tr><tr><td>工号</td><td th:text="${num}"></td></tr><tr><td>薪水</td><td th:text="${salary}"></td></tr>
</table>
<div style="color: #ff1a0e">一起努力创造辉煌</div>
</body>
</html>

接下来发送邮件:

@Autowired
TemplateEngine templateEngine;@Test
public void sendThymeleafMail() throws MessagingException {MimeMessage mimeMessage = javaMailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setSubject("这是一封测试邮件");helper.setFrom("1510161612@qq.com");helper.setTo("25xxxxx755@qq.com");helper.setCc("37xxxxx37@qq.com");helper.setBcc("14xxxxx098@qq.com");helper.setSentDate(new Date());Context context = new Context();context.setVariable("username", "javaboy");context.setVariable("num","000001");context.setVariable("salary", "99999");String process = templateEngine.process("mail.html", context);helper.setText(process,true);javaMailSender.send(mimeMessage);
}

调用该方法,发送邮件,效果图如下:

好了,这就是我们今天说的 5 种邮件发送姿势,不知道你掌握了没有呢?

本文案例已经上传到 GitHub:https://github.com/lenve/javaboy-code-samples。

有问题欢迎留言讨论。

参考资料:

  • https://www.mailgun.com/blog/which-smtp-port-understanding-ports-25-465-587

这篇关于Spring Boot 发送邮件,端口号大有玄机的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/370139

相关文章

Django开发时如何避免频繁发送短信验证码(python图文代码)

《Django开发时如何避免频繁发送短信验证码(python图文代码)》Django开发时,为防止频繁发送验证码,后端需用Redis限制请求频率,结合管道技术提升效率,通过生产者消费者模式解耦业务逻辑... 目录避免频繁发送 验证码1. www.chinasem.cn避免频繁发送 验证码逻辑分析2. 避免频繁

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

破茧 JDBC:MyBatis 在 Spring Boot 中的轻量实践指南

《破茧JDBC:MyBatis在SpringBoot中的轻量实践指南》MyBatis是持久层框架,简化JDBC开发,通过接口+XML/注解实现数据访问,动态代理生成实现类,支持增删改查及参数... 目录一、什么是 MyBATis二、 MyBatis 入门2.1、创建项目2.2、配置数据库连接字符串2.3、入

Springboot项目启动失败提示找不到dao类的解决

《Springboot项目启动失败提示找不到dao类的解决》SpringBoot启动失败,因ProductServiceImpl未正确注入ProductDao,原因:Dao未注册为Bean,解决:在启... 目录错误描述原因解决方法总结***************************APPLICA编

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

Apache Ignite 与 Spring Boot 集成详细指南

《ApacheIgnite与SpringBoot集成详细指南》ApacheIgnite官方指南详解如何通过SpringBootStarter扩展实现自动配置,支持厚/轻客户端模式,简化Ign... 目录 一、背景:为什么需要这个集成? 二、两种集成方式(对应两种客户端模型) 三、方式一:自动配置 Thick

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与