掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
今天想和大家聊一下 SMTP 服務(wù)器的端口問(wèn)題,這個(gè)也是一個(gè)小伙伴提的問(wèn)題,SMTP 服務(wù)器有眾多端口:25、465、587 各自間有什么區(qū)別?可以隨意使用嗎?希望今天這篇文章能給你答案。

創(chuàng)新互聯(lián)長(zhǎng)期為上千多家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為榮縣企業(yè)提供專(zhuān)業(yè)的做網(wǎng)站、成都網(wǎng)站制作,榮縣網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
SMTP 代表簡(jiǎn)單郵件傳輸協(xié)議,簡(jiǎn)而言之,它是通過(guò) Internet 發(fā)送電子郵件的過(guò)程。計(jì)算機(jī)端口是個(gè)人計(jì)算機(jī)連接到網(wǎng)絡(luò)并完成數(shù)據(jù)傳輸?shù)姆绞?。SMTP 端口是兩者的組合:設(shè)計(jì)用于通過(guò)網(wǎng)絡(luò)向其收件人發(fā)送電子郵件的端口。
下圖展示了 SMTP 協(xié)議在郵件發(fā)送過(guò)程的作用:
當(dāng)然,就像有多個(gè)計(jì)算機(jī)端口一樣,可以使用的 SMTP 端口也有很多。
1982 年,南加州大學(xué)向 Internet 工程任務(wù)組 (IETF) 提交了一份提案,即 Request For Comments (RFC) 821,將端口 25 建立為 Internet 電子郵件的默認(rèn)傳輸通道。40 年過(guò)去了,如今我們依然可以使用 25 這個(gè)端口在兩個(gè)郵件服務(wù)器之間傳輸郵件。
不過(guò)最初的設(shè)計(jì)沒(méi)有考慮安全問(wèn)題,在 1998 年 12 月,R. Gellens 和 J. Klensin 提交了 RFC2476,在這個(gè)規(guī)范中,RFC 提議將傳統(tǒng)的消息提交和消息中繼概念分開(kāi),RFC 定義消息提交應(yīng)通過(guò)端口 587 進(jìn)行(即我們通過(guò)郵件客戶端等工具提交郵件的時(shí)候,應(yīng)該使用 587 端口),以確保新的策略和安全要求不會(huì)干擾消息中繼端口 25 上的傳統(tǒng)中繼流量。
這么一拆分,端口 25 就主要用于 SMTP 中繼,也就是將郵件從一個(gè)電子郵件服務(wù)器傳輸?shù)搅硪粋€(gè)電子郵件服務(wù)器。
在大多數(shù)情況下,SMTP 電子郵件客戶端(Foxmail、Microsoft Outlook、Mail、Thunderbird 等)不應(yīng)使用 25 端口,以遏制垃圾郵件的數(shù)量,所以這個(gè) 25 端口和我們個(gè)人使用的關(guān)系就不大。
這是默認(rèn)的郵件提交端口,當(dāng)用戶提交一封電子郵件到郵件服務(wù)器時(shí),可以使用該端口,我們自己通過(guò) Java 代碼發(fā)送郵件,也可以使用該端口。
端口 587 與 TLS 加密相結(jié)合,可確保安全提交電子郵件并遵循 IETF 制定的指導(dǎo)方針。
那按理說(shuō)我們發(fā)送郵件的時(shí)候就該使用 587 端口呀,465 又是干嘛的?
IETF 從未將端口 465 發(fā)布為官方 SMTP 傳輸或提交端口,然而維護(hù)大部分核心互聯(lián)網(wǎng)基礎(chǔ)設(shè)施的 IANA 為 SMTPS 分配了端口 465。目的是為 SMTP 建立一個(gè)端口,以便使用安全套接字層 (SSL) 進(jìn)行操作,這樣使得郵件發(fā)送更加安全。
所以 465 和 587 其實(shí)都是為了郵件安全,但是兩者的思路不一樣,465 是 SSL,587 則是 TLS,SSL 和 TLS 有啥區(qū)別呢?這個(gè)就說(shuō)來(lái)話長(zhǎng)了,簡(jiǎn)單一句話就是:TLS(傳輸層安全)是更為安全的升級(jí)版 SSL,TLS 是 SSL 標(biāo)準(zhǔn)化后的產(chǎn)物。
按理說(shuō) 465 應(yīng)該被撤銷(xiāo),大家都用 587,但是由于 465 曾經(jīng)被 IANA 認(rèn)定為有效,因此可能存在僅能夠使用此端口連接的遺留系統(tǒng),所以該端口并沒(méi)有被廢棄,也可以使用。
好啦,這就是這幾個(gè)端口的區(qū)別。一般來(lái)說(shuō),我們用 Spring Boot 發(fā)送郵件的時(shí)候,465 和 587 都能用,但是不建議使用 25。另外在使用 465 或者 587 的時(shí)候,有的個(gè)別郵箱如 139 郵箱需要配置如下屬性:
spring.mail.properties.mail.smtp.ssl.enable=true
可能還有小伙伴不懂 Spring Boot 郵件發(fā)送,再來(lái)回顧下。
郵件發(fā)送其實(shí)是一個(gè)非常常見(jiàn)的需求,用戶注冊(cè),找回密碼等地方,都會(huì)用到,使用 JavaSE 代碼發(fā)送郵件,步驟還是挺繁瑣的,Spring Boot 中對(duì)于郵件發(fā)送,提供了相關(guān)的自動(dòng)化配置類(lèi),使得郵件發(fā)送變得非常容易,接下來(lái)我們就來(lái)一探究竟!看看使用 Spring Boot 發(fā)送郵件的 5 中姿勢(shì)。
我們經(jīng)常會(huì)聽(tīng)到各種各樣的郵件協(xié)議,比如 SMTP、POP3、IMAP ,那么這些協(xié)議有什么作用,有什么區(qū)別?我們先來(lái)討論一下這個(gè)問(wèn)題。
SMTP 是一個(gè)基于 TCP/IP 的應(yīng)用層協(xié)議,江湖地位有點(diǎn)類(lèi)似于 HTTP,SMTP 服務(wù)器默認(rèn)監(jiān)聽(tīng)的端口號(hào)為 25 ??吹竭@里,小伙伴們可能會(huì)想到既然 SMTP 協(xié)議是基于 TCP/IP 的應(yīng)用層協(xié)議,那么我是不是也可以通過(guò) Socket 發(fā)送一封郵件呢?回答是肯定的。
生活中我們投遞一封郵件要經(jīng)過(guò)如下幾個(gè)步驟:
這是一個(gè)縮減版的生活中郵件發(fā)送過(guò)程。這三個(gè)步驟可以分別對(duì)應(yīng)我們的郵件發(fā)送過(guò)程,假設(shè)從 [email protected] 發(fā)送郵件到 [email protected] :
郵件投遞大致就是這個(gè)過(guò)程,這個(gè)過(guò)程就涉及到了多個(gè)協(xié)議,我們來(lái)分別看一下。
SMTP 協(xié)議全稱(chēng)為 Simple Mail Transfer Protocol,譯作簡(jiǎn)單郵件傳輸協(xié)議,它定義了郵件客戶端軟件與 SMTP 服務(wù)器之間,以及 SMTP 服務(wù)器與 SMTP 服務(wù)器之間的通信規(guī)則。
也就是說(shuō) [email protected] 用戶先將郵件投遞到騰訊的 SMTP 服務(wù)器這個(gè)過(guò)程就使用了 SMTP 協(xié)議,然后騰訊的 SMTP 服務(wù)器將郵件投遞到網(wǎng)易的 SMTP 服務(wù)器這個(gè)過(guò)程也依然使用了 SMTP 協(xié)議,SMTP 服務(wù)器就是用來(lái)收郵件。
而 POP3 協(xié)議全稱(chēng)為 Post Office Protocol ,譯作郵局協(xié)議,它定義了郵件客戶端與 POP3 服務(wù)器之間的通信規(guī)則,那么該協(xié)議在什么場(chǎng)景下會(huì)用到呢?當(dāng)郵件到達(dá)網(wǎng)易的 SMTP 服務(wù)器之后, [email protected] 用戶需要登錄服務(wù)器查看郵件,這個(gè)時(shí)候就該協(xié)議就用上了:郵件服務(wù)商都會(huì)為每一個(gè)用戶提供專(zhuān)門(mén)的郵件存儲(chǔ)空間,SMTP 服務(wù)器收到郵件之后,就將郵件保存到相應(yīng)用戶的郵件存儲(chǔ)空間中,如果用戶要讀取郵件,就需要通過(guò)郵件服務(wù)商的 POP3 郵件服務(wù)器來(lái)完成。
最后,可能也有小伙伴們聽(tīng)說(shuō)過(guò) IMAP 協(xié)議,這個(gè)協(xié)議是對(duì) POP3 協(xié)議的擴(kuò)展,功能更強(qiáng),作用類(lèi)似,這里不再贅述。
目前國(guó)內(nèi)大部分的郵件服務(wù)商都不允許直接使用用戶名/密碼的方式來(lái)在代碼中發(fā)送郵件,都是要先申請(qǐng)授權(quán)碼,這里以 QQ 郵箱為例,向大家演示授權(quán)碼的申請(qǐng)流程:首先我們需要先登錄 QQ 郵箱網(wǎng)頁(yè)版,點(diǎn)擊上方的設(shè)置按鈕:
然后點(diǎn)擊賬戶選項(xiàng)卡:
在賬戶選項(xiàng)卡中找到開(kāi)啟POP3/SMTP選項(xiàng),如下:
點(diǎn)擊開(kāi)啟,開(kāi)啟相關(guān)功能,開(kāi)啟過(guò)程需要手機(jī)號(hào)碼驗(yàn)證,按照步驟操作即可,不贅述。開(kāi)啟成功之后,即可獲取一個(gè)授權(quán)碼,將該號(hào)碼保存好,一會(huì)使用。
接下來(lái),我們就可以創(chuàng)建項(xiàng)目了,Spring Boot 中,對(duì)于郵件發(fā)送提供了自動(dòng)配置類(lèi),開(kāi)發(fā)者只需要加入相關(guān)依賴(lài),然后配置一下郵箱的基本信息,就可以發(fā)送郵件了。
首先創(chuàng)建一個(gè) Spring Boot 項(xiàng)目,引入郵件發(fā)送依賴(lài):
創(chuàng)建完成后,項(xiàng)目依賴(lài)如下:
org.springframework.boot
spring-boot-starter-mail
org.springframework.boot
spring-boot-starter-web
配置郵箱基本信息
項(xiàng)目創(chuàng)建成功后,接下來(lái)在 application.properties 中配置郵箱的基本信息:
spring.mail.host=smtp.qq.com
spring.mail.port=587
[email protected]
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 服務(wù)器的端口或者地址的的話,可以參考 騰訊的郵箱文檔
??https://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=371??
做完這些之后,Spring Boot 就會(huì)自動(dòng)幫我們配置好郵件發(fā)送類(lèi),相關(guān)的配置在 org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration 類(lèi)中,部分源碼如下:
@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 {
}
從這段代碼中,可以看到,導(dǎo)入了另外一個(gè)配置 MailSenderPropertiesConfiguration類(lèi),這個(gè)類(lèi)中,提供了郵件發(fā)送相關(guān)的工具類(lèi):
@Configuration
@ConditionalOnProperty(prefix = "spring.mail", name = "host")
class MailSenderPropertiesConfiguration {
private final MailProperties properties;
MailSenderPropertiesConfiguration(MailProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public JavaMailSenderImpl mailSender() {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
applyProperties(sender);
return sender;
}
}
可以看到,這里創(chuàng)建了一個(gè) JavaMailSenderImpl 的實(shí)例, JavaMailSenderImpl 是 JavaMailSender 的一個(gè)實(shí)現(xiàn),我們將使用 JavaMailSenderImpl 來(lái)完成郵件的發(fā)送工作。
做完如上兩步,郵件發(fā)送的準(zhǔn)備工作就算是完成了,接下來(lái)就可以直接發(fā)送郵件了。
具體的發(fā)送,有 5 種不同的方式,我們一個(gè)一個(gè)來(lái)看。
簡(jiǎn)單郵件就是指郵件內(nèi)容是一個(gè)普通的文本文檔:
@Autowired
JavaMailSender javaMailSender;
@Test
public void sendSimpleMail() {
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject("這是一封測(cè)試郵件");
message.setFrom("[email protected]");
message.setTo("[email protected]");
message.setCc("[email protected]");
message.setBcc("[email protected]");
message.setSentDate(new Date());
message.setText("這是測(cè)試郵件的正文");
javaMailSender.send(message);
}
從上往下,代碼含義分別如下:
最后執(zhí)行該方法,就可以實(shí)現(xiàn)郵件的發(fā)送,發(fā)送效果圖如下:
郵件的附件可以是圖片,也可以是普通文件,都是支持的。
@Test
public void sendAttachFileMail() throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
helper.setSubject("這是一封測(cè)試郵件");
helper.setFrom("[email protected]");
helper.setTo("[email protected]");
helper.setCc("[email protected]");
helper.setBcc("[email protected]");
helper.setSentDate(new Date());
helper.setText("這是測(cè)試郵件的正文");
helper.addAttachment("javaboy.jpg",new File("C:\\Users\\sang\\Downloads\\javaboy.png"));
javaMailSender.send(mimeMessage);
}
注意這里在構(gòu)建郵件對(duì)象上和前文有所差異,這里是通過(guò) javaMailSender 來(lái)獲取一個(gè)復(fù)雜郵件對(duì)象,然后再利用 MimeMessageHelper 對(duì)郵件進(jìn)行配置,MimeMessageHelper 是一個(gè)郵件配置的輔助工具類(lèi),創(chuàng)建時(shí)候的 true 表示構(gòu)建一個(gè) multipart message 類(lèi)型的郵件,有了 MimeMessageHelper 之后,我們針對(duì)郵件的配置都是由 MimeMessageHelper 來(lái)代勞。
最后通過(guò) addAttachment 方法來(lái)添加一個(gè)附件。
執(zhí)行該方法,郵件發(fā)送效果圖如下:
圖片資源和附件有什么區(qū)別呢?圖片資源是放在郵件正文中的,即一打開(kāi)郵件,就能看到圖片。但是一般來(lái)說(shuō),不建議使用這種方式,一些公司會(huì)對(duì)郵件內(nèi)容的大小有限制(因?yàn)檫@種方式是將圖片一起發(fā)送的)。
@Test
public void sendImgResMail() throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("這是一封測(cè)試郵件");
helper.setFrom("[email protected]");
helper.setTo("[email protected]");
helper.setCc("[email protected]");
helper.setBcc("[email protected]");
helper.setSentDate(new Date());
helper.setText("hello 大家好,這是一封測(cè)試郵件,這封郵件包含兩種圖片,分別如下
第一張圖片:
第二張圖片:
",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 是一個(gè) HTML 文本,里邊涉及到的圖片資源先用一個(gè)占位符占著,setText 方法的第二個(gè)參數(shù) true 表示第一個(gè)參數(shù)是一個(gè) HTML 文本。
setText 之后,再通過(guò) addInline 方法來(lái)添加圖片資源。
最后執(zhí)行該方法,發(fā)送郵件,效果如下:
在公司實(shí)際開(kāi)發(fā)中,第一種和第三種都不是使用最多的郵件發(fā)送方案。因?yàn)檎?lái)說(shuō),郵件的內(nèi)容都是比較的豐富的,所以大部分郵件都是通過(guò) HTML 來(lái)呈現(xiàn)的,如果直接拼接 HTML 字符串,這樣以后不好維護(hù),為了解決這個(gè)問(wèn)題,一般郵件發(fā)送,都會(huì)有相應(yīng)的郵件模板。最具代表性的兩個(gè)模板就是 Freemarker 模板和 Thyemeleaf 模板了。
首先需要引入 Freemarker 依賴(lài):
org.springframework.boot
spring-boot-starter-freemarker
然后在 resources/templates 目錄下創(chuàng)建一個(gè) mail.ftl 作為郵件發(fā)送模板:
Title hello 歡迎加入 xxx 大家庭,您的入職信息如下:
| 姓名 | ${username} |
| 工號(hào) | ${num} |
| 薪水 | ${salary} |
接下來(lái),將郵件模板渲染成 HTML ,然后發(fā)送即可。
@Test
public void sendFreemarkerMail() throws MessagingException, IOException, TemplateException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("這是一封測(cè)試郵件");
helper.setFrom("[email protected]");
helper.setTo("[email protected]");
helper.setCc("[email protected]");
helper.setBcc("[email protected]");
helper.setSentDate(new Date());
//構(gòu)建 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();
//模板渲染,渲染的結(jié)果將被保存到 out 中 ,將out 中的 html 字符串發(fā)送即可
template.process(user, out);
helper.setText(out.toString(),true);
javaMailSender.send(mimeMessage);
}
需要注意的是,雖然引入了 Freemarker 的自動(dòng)化配置,但是我們?cè)谶@里是直接 new Configuration 來(lái)重新配置 Freemarker的,所以 Freemarker 默認(rèn)的配置這里不生效,因此,在填寫(xiě)模板位置時(shí),值為 templates 。
調(diào)用該方法,發(fā)送郵件,效果圖如下:
推薦在 Spring Boot 中使用 Thymeleaf 來(lái)構(gòu)建郵件模板。因?yàn)?Thymeleaf 的自動(dòng)化配置提供了一個(gè) TemplateEngine,通過(guò) TemplateEngine 可以方便的將 Thymeleaf 模板渲染為 HTML ,同時(shí),Thymeleaf 的自動(dòng)化配置在這里是繼續(xù)有效的 。
首先,引入 Thymeleaf 依賴(lài):
org.springframework.boot
spring-boot-starter-thymeleaf
然后,創(chuàng)建 Thymeleaf 郵件模板:
Title hello 歡迎加入 xxx 大家庭,您的入職信息如下:
| 姓名 | |
| 工號(hào) | |
| 薪水 |
接下來(lái)發(fā)送郵件:
@Autowired
TemplateEngine templateEngine;
@Test
public void sendThymeleafMail() throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("這是一封測(cè)試郵件");
helper.setFrom("[email protected]");
helper.setTo("[email protected]");
helper.setCc("[email protected]");
helper.setBcc("[email protected]");
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);
}
調(diào)用該方法,發(fā)送郵件,效果圖如下:
好了,這就是我們今天說(shuō)的 5 種郵件發(fā)送姿勢(shì),不知道你掌握了沒(méi)有呢?
本文案例已經(jīng)上傳到 GitHub:https://github.com/lenve/javaboy-code-samples。
有問(wèn)題歡迎留言討論。
參考資料:
https://www.mailgun.com/blog/which-smtp-port-understanding-ports-25-465-587
本文轉(zhuǎn)載自微信公眾號(hào)「江南一點(diǎn)雨」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系江南一點(diǎn)雨公眾號(hào)。

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流