[TOC]
0x00 简述前言 描述:最近正好在学邮件发信与激活的小程序开发,顺便把邮箱发信的基础知识理一理方便后续的开发工作以及安全相关的一些知识;
1.邮箱简述 Q:什么是邮件服务器? 答:它可以类似于现实生活中的邮局投递用户的邮件,电子邮件是Intenet基本服务之一,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱,但是前提是需要在服务器上安装相应的邮件服务器应用; 使用电子邮件系统可以实现不受时间和空间限制的信息传递额交流;
常见的邮件服务提供商: Sina , QQ , 163 / 126 , Foxmail, 189, aliyun ,Outlook,Gmail
等等;
邮件服务器:按照提供的服务类型可以分为发送邮件的服务器
和接收邮件的服务器
。
Q:什么是电子邮箱? 答:即我们在邮件服务提供商或者说是邮件服务器上申请建立的一个账户(相当于收货地址),并且为每一个用户分配一定的空间用于保存发送的电子邮件和接收到的电子邮件;
Q:如何搭建自己的邮件服务器?需要那些技术? 答:这将是本文的核心思想,从下面的基础实例中的可以从搭建到使用。 邮件服务器需求:
服务器
邮件服务端(易邮)和客户端(foxmail)
域名(DNS / A / MX / CNAME)
什么是 A 记录?
A (Address) 记录是用来指定主机名(或域名)对应的 IP 地址记录。用户可以将该域名下的主机名(二级域名)指向到自己的 服务器上。
什么是别名记录(CNAME)?
什么是 MX 记录?(重点)
MX(Mail Exchanger)记录是邮件交换记录,它指向一个邮件服务器,用于电子邮件系统发邮件时根据收信人的地址后缀来定位邮件服务器。例如,当 Internet 上的某用户要发一封信给 user@mydomain.com 时,该用户的邮件系统通过 DNS 查找 mydomain.com 这个域名的 MX 记录,如果 MX 记录存在, 用户计算机就将邮件发送到MX记录所指定的邮件服务器上。
Q:邮件协议的介绍: 描述:邮件协议作用是约定了邮件在网络中传输格式,便于接收发送邮件双方可以正常看到对方所发的信息(实际上是解码
) 常用的邮件协议有两种(发送和接收):
发送:
SMTP 协议-发邮件协议,全称为 Simple Mail Transfer Protoco(简单邮件传输协议),它定义了邮件客户端软件与 SMTP 服务器之间、以及两台 SMTP 服务器之间的通讯规则。
SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。SMTP 服务器就是遵循 SMTP 协议的发送邮件服务器。
SMTP 认证简单地说就是要求必须在提供了账户名和密码之后才可以登录 SMTP 服务器,增加 SMTP 认证的目的是为了使用户避免受到垃圾邮件的侵扰。
端口: 465/994 , 25接收:
POP3 协议-收邮件协议,全称为Post Office Protocol
(邮局协议)即第3个版本,它定义了邮件客户端软件与 POP3 服务器的通讯规 则。
它是因特网电子邮件的第一个离线协议标准,POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,同时删除保存在邮件服务器上的邮件而POP3服务器则是遵循POP3协议的接收邮件服务器,用来接收电子邮件的。
端口: 995, 110
IMAP 协议-交互式存取邮件协议,全称是 Internet Mail Access Protocol
,它是跟POP3类似邮件访问标准协议之一。
开启了IMAP后您在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器
上,如:删除邮件,标记已读等,服务器上的邮件也会做相应的动
作。所以无论从浏览器登录邮箱或者客户端软件登录邮箱,看到的邮件以及状态都是一致的。
端口: 993, 143
weiyigeek.top-协议端口信息
总结:
1.POP3 与 IMAP 的区别?
POP3协议允许电子邮件客户端下载服务器上的邮件,但是在客户端的操作(如移动邮件、标记已读等),不会反馈到服务器上。 IMAP协议提供与电子邮件客户端之间的双向通信,客户端的操作都会反馈到服务器上,对邮件进行的操作服务器上的邮件也会做相应的动作。同时它也像pop3支持邮件下载服务,让用户进行离线阅读; 它还提供的摘要浏览功能可以让你在阅读完所有的邮件到达时间、主题、发件人、大小等信息后才作出是否下载的决定,更好地支持了从多个不同设备中随时访问新邮件。 总之IMAP 整体上为用户带来更为便捷和可靠的体验
。POP3 更易丢失邮件或多次下载相同的邮件
,但 IMAP 通过邮件客户端与webmail 之间的双向同步功能很好地避免了这些问题。
weiyigeek.top-区别对应
邮箱发送流程
weiyigeek.top-发送流程
2.MIME的编码 描述:说到邮件就不得不提到MIME的编码介绍(base64)及使用的意义
2.1 MIME: Multipurpose Internet Mail Extensions 描述:英国帝国大学计算机在线字典FOLDOC对MIME的解释为:多部分(multi-part)、多媒体电子邮件和WWW超文本的一种编码标准,用于传送诸如图形、声音和传真等非文本数据。MIME定义于RFC1341,用MIMENCODE的方法将二进制数据转换成为一种被称为BASE64的ASCII子集的字符的组合。
Internet上有专门讨论MIME的新闻组:comp.mail.mime
MIMENCODE最早称为MMENCODE提出用MIMENCODE代替UUENCODE,是因为UUENCODE使用了一些字符在一些邮件网关(特别是那些转换ASCII和EBCDIC码的网关)中造成传输障碍,(还有一些软件不能对所有 UUENCODE 的算法进行正确解码而导致邮件的阅读困难),因此 MIME 被设计用于替代UUENCODE,但是结果是这些协议共存。
在MIME出台之前,使用RFC 822只能发送基本的ASCII码文本信息,邮件内容如果要包括二进制文件、声音和动画等,实现起来非常困难。MIME提供了一种可以在邮件中附加多种不同编码文件的方法,弥补了原来的信息格式的不足。实际上不仅仅是邮件编码,现在MIME经成为HTTP协议标准的一个部分 。
2.2 MIME编码方式简介 描述:对邮件进行编码最初的原因是因为 Internet 上的很多网关不能正确传输8bit内码的字符,比如汉字等。编码的原理就是把8bit的内容转换成7bit的形式以能正确传输,在接收方收到之后,再将其还原成8bit的内容。
在MIME协议之前,邮件的编码曾经有过UUENCODE等编码方式 ,但是由于MIME协议算法简单,并且易于扩展,现在已经成为邮件编码方式的主流,不仅是用来传输8bit的字符,也可以用来传送二进制的文件,如邮件附件中的图像、音频等信息,而且扩展了很多基于MIME 的应用。 从编码方式来说,MIME定义了两种编码方法Base64与QP(Quote-Printable)
。
1.Base64编码 描述:Base64是一种通用的方法,其原理很简单,就是把三个Byte的数据用4个Byte表示。在这四个Byte中,实际用到的都只有前面6bit,这样就不存在只能传输7bit的字符的问题了。Base64的缩写一般是B。 Base64将输入的字符串或一段数据编码成只含有{‘A’-‘Z’, ‘a’-‘z’, ‘0’-‘9’, ‘+’, ‘/‘}这64个字符的串,’=’用于填充。 其编码的方法是,将输入数据流每次取6bit,用此6bit的值(0-63)作为索引去查表,输出相应字符。 这样每3个字节将编码为4个字符(3×8 → 4×6);不满4个字符的以’=’填充。 有的场合,以“=?charset?B?xxxxxxxx?=”
表示xxxxxxxx是Base64编码,且原文的字符集是charset。在段体内则直接编码,适当时机换行,MIME建议每行最多76个字符。 Base64的算法很简单,它将字符流顺序放入一个24位的缓冲区,缺字符的地方补零。 然后将缓冲区截断成为4个部分,高位在先,每个部分6位,用64个字符重新表示。如果输入只有一个或两个字节,那么输出将用等号“=”补足。这可以隔断附加的信息造成编码的混乱。
2.QP编码 描述:另一种方法是QP(Quote-Printable) 方法,通常缩写为“Q”方法,其原理是把一个8bit的字符用两个16进制数值表示,然后在前面加“=”。所以我们看到经过QP编码后的文件通常是这个样子:=B3=C2=BF=A1=C7=E5=A3= AC=C4=FA=BA=C3=A3=A1
。 Quoted-printable根据输入的字符串或字节范围进行编码,若是不需编码的字符直接输出。若需要编码则先输出’=’后面跟着以2个字符表示的十六进制字节值。有的场合以“=?charset?Q?xxxxxxxx?=”
表示xxxxxxxx是Quoted-printable编码,且原文的字符集是charset。在段体内则直接编码适当时机换行,换行前额外输出一个’=’。
2.3MIME的头信息 描述:邮件头在邮件头中有很多从RFC 822沿用的域名MIME也增加了一些。
常见的标准域名和含义如下:
[TOC]
0x00 简述前言 描述:最近正好在学邮件发信与激活的小程序开发,顺便把邮箱发信的基础知识理一理方便后续的开发工作以及安全相关的一些知识;
1.邮箱简述 Q:什么是邮件服务器? 答:它可以类似于现实生活中的邮局投递用户的邮件,电子邮件是Intenet基本服务之一,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱,但是前提是需要在服务器上安装相应的邮件服务器应用; 使用电子邮件系统可以实现不受时间和空间限制的信息传递额交流;
常见的邮件服务提供商: Sina , QQ , 163 / 126 , Foxmail, 189, aliyun ,Outlook,Gmail
等等;
邮件服务器:按照提供的服务类型可以分为发送邮件的服务器
和接收邮件的服务器
。
Q:什么是电子邮箱? 答:即我们在邮件服务提供商或者说是邮件服务器上申请建立的一个账户(相当于收货地址),并且为每一个用户分配一定的空间用于保存发送的电子邮件和接收到的电子邮件;
Q:如何搭建自己的邮件服务器?需要那些技术? 答:这将是本文的核心思想,从下面的基础实例中的可以从搭建到使用。 邮件服务器需求:
服务器
邮件服务端(易邮)和客户端(foxmail)
域名(DNS / A / MX / CNAME)
什么是 A 记录?
A (Address) 记录是用来指定主机名(或域名)对应的 IP 地址记录。用户可以将该域名下的主机名(二级域名)指向到自己的 服务器上。
什么是别名记录(CNAME)?
什么是 MX 记录?(重点)
MX(Mail Exchanger)记录是邮件交换记录,它指向一个邮件服务器,用于电子邮件系统发邮件时根据收信人的地址后缀来定位邮件服务器。例如,当 Internet 上的某用户要发一封信给 user@mydomain.com 时,该用户的邮件系统通过 DNS 查找 mydomain.com 这个域名的 MX 记录,如果 MX 记录存在, 用户计算机就将邮件发送到MX记录所指定的邮件服务器上。
Q:邮件协议的介绍: 描述:邮件协议作用是约定了邮件在网络中传输格式,便于接收发送邮件双方可以正常看到对方所发的信息(实际上是解码
) 常用的邮件协议有两种(发送和接收):
发送:
SMTP 协议-发邮件协议,全称为 Simple Mail Transfer Protoco(简单邮件传输协议),它定义了邮件客户端软件与 SMTP 服务器之间、以及两台 SMTP 服务器之间的通讯规则。
SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。SMTP 服务器就是遵循 SMTP 协议的发送邮件服务器。
SMTP 认证简单地说就是要求必须在提供了账户名和密码之后才可以登录 SMTP 服务器,增加 SMTP 认证的目的是为了使用户避免受到垃圾邮件的侵扰。
端口: 465/994 , 25接收:
POP3 协议-收邮件协议,全称为Post Office Protocol
(邮局协议)即第3个版本,它定义了邮件客户端软件与 POP3 服务器的通讯规 则。
它是因特网电子邮件的第一个离线协议标准,POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,同时删除保存在邮件服务器上的邮件而POP3服务器则是遵循POP3协议的接收邮件服务器,用来接收电子邮件的。
端口: 995, 110
IMAP 协议-交互式存取邮件协议,全称是 Internet Mail Access Protocol
,它是跟POP3类似邮件访问标准协议之一。
开启了IMAP后您在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器
上,如:删除邮件,标记已读等,服务器上的邮件也会做相应的动
作。所以无论从浏览器登录邮箱或者客户端软件登录邮箱,看到的邮件以及状态都是一致的。
端口: 993, 143
weiyigeek.top-协议端口信息
总结:
1.POP3 与 IMAP 的区别?
POP3协议允许电子邮件客户端下载服务器上的邮件,但是在客户端的操作(如移动邮件、标记已读等),不会反馈到服务器上。 IMAP协议提供与电子邮件客户端之间的双向通信,客户端的操作都会反馈到服务器上,对邮件进行的操作服务器上的邮件也会做相应的动作。同时它也像pop3支持邮件下载服务,让用户进行离线阅读; 它还提供的摘要浏览功能可以让你在阅读完所有的邮件到达时间、主题、发件人、大小等信息后才作出是否下载的决定,更好地支持了从多个不同设备中随时访问新邮件。 总之IMAP 整体上为用户带来更为便捷和可靠的体验
。POP3 更易丢失邮件或多次下载相同的邮件
,但 IMAP 通过邮件客户端与webmail 之间的双向同步功能很好地避免了这些问题。
weiyigeek.top-区别对应
邮箱发送流程
weiyigeek.top-发送流程
2.MIME的编码 描述:说到邮件就不得不提到MIME的编码介绍(base64)及使用的意义
2.1 MIME: Multipurpose Internet Mail Extensions 描述:英国帝国大学计算机在线字典FOLDOC对MIME的解释为:多部分(multi-part)、多媒体电子邮件和WWW超文本的一种编码标准,用于传送诸如图形、声音和传真等非文本数据。MIME定义于RFC1341,用MIMENCODE的方法将二进制数据转换成为一种被称为BASE64的ASCII子集的字符的组合。
Internet上有专门讨论MIME的新闻组:comp.mail.mime
MIMENCODE最早称为MMENCODE提出用MIMENCODE代替UUENCODE,是因为UUENCODE使用了一些字符在一些邮件网关(特别是那些转换ASCII和EBCDIC码的网关)中造成传输障碍,(还有一些软件不能对所有 UUENCODE 的算法进行正确解码而导致邮件的阅读困难),因此 MIME 被设计用于替代UUENCODE,但是结果是这些协议共存。
在MIME出台之前,使用RFC 822只能发送基本的ASCII码文本信息,邮件内容如果要包括二进制文件、声音和动画等,实现起来非常困难。MIME提供了一种可以在邮件中附加多种不同编码文件的方法,弥补了原来的信息格式的不足。实际上不仅仅是邮件编码,现在MIME经成为HTTP协议标准的一个部分 。
2.2 MIME编码方式简介 描述:对邮件进行编码最初的原因是因为 Internet 上的很多网关不能正确传输8bit内码的字符,比如汉字等。编码的原理就是把8bit的内容转换成7bit的形式以能正确传输,在接收方收到之后,再将其还原成8bit的内容。
在MIME协议之前,邮件的编码曾经有过UUENCODE等编码方式 ,但是由于MIME协议算法简单,并且易于扩展,现在已经成为邮件编码方式的主流,不仅是用来传输8bit的字符,也可以用来传送二进制的文件,如邮件附件中的图像、音频等信息,而且扩展了很多基于MIME 的应用。 从编码方式来说,MIME定义了两种编码方法Base64与QP(Quote-Printable)
。
1.Base64编码 描述:Base64是一种通用的方法,其原理很简单,就是把三个Byte的数据用4个Byte表示。在这四个Byte中,实际用到的都只有前面6bit,这样就不存在只能传输7bit的字符的问题了。Base64的缩写一般是B。 Base64将输入的字符串或一段数据编码成只含有{‘A’-‘Z’, ‘a’-‘z’, ‘0’-‘9’, ‘+’, ‘/‘}这64个字符的串,’=’用于填充。 其编码的方法是,将输入数据流每次取6bit,用此6bit的值(0-63)作为索引去查表,输出相应字符。 这样每3个字节将编码为4个字符(3×8 → 4×6);不满4个字符的以’=’填充。 有的场合,以“=?charset?B?xxxxxxxx?=”
表示xxxxxxxx是Base64编码,且原文的字符集是charset。在段体内则直接编码,适当时机换行,MIME建议每行最多76个字符。 Base64的算法很简单,它将字符流顺序放入一个24位的缓冲区,缺字符的地方补零。 然后将缓冲区截断成为4个部分,高位在先,每个部分6位,用64个字符重新表示。如果输入只有一个或两个字节,那么输出将用等号“=”补足。这可以隔断附加的信息造成编码的混乱。
2.QP编码 描述:另一种方法是QP(Quote-Printable) 方法,通常缩写为“Q”方法,其原理是把一个8bit的字符用两个16进制数值表示,然后在前面加“=”。所以我们看到经过QP编码后的文件通常是这个样子:=B3=C2=BF=A1=C7=E5=A3= AC=C4=FA=BA=C3=A3=A1
。 Quoted-printable根据输入的字符串或字节范围进行编码,若是不需编码的字符直接输出。若需要编码则先输出’=’后面跟着以2个字符表示的十六进制字节值。有的场合以“=?charset?Q?xxxxxxxx?=”
表示xxxxxxxx是Quoted-printable编码,且原文的字符集是charset。在段体内则直接编码适当时机换行,换行前额外输出一个’=’。
2.3MIME的头信息 描述:邮件头在邮件头中有很多从RFC 822沿用的域名MIME也增加了一些。
常见的标准域名和含义如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Received 传输路径 各级邮件服务器 Return-Path 回复地址 目标邮件服务器 Delivered-To 发送地址 目标邮件服务器 Reply-To 回复地址 邮件的创建者 From 发件人地址 邮件的创建者 To 收件人地址 邮件的创建者 Cc 抄送地址 邮件的创建者 Bcc 暗送地址 邮件的创建者 Date 日期和时间 邮件的创建者 Subject 主题 邮件的创建者 Message-ID 消息ID 邮件的创建者 MIME-Version MIME版本 邮件的创建者 Content-Type 内容的类型 邮件的创建者 Content-Transfer-Encoding 内容的传输编码方式 邮件的创建者 段头 Content-Type 段体的类型 Content-Transfer-Encoding 段体的传输编码方式 Content-Disposition 段体的安排方式 Content-ID 段体的ID Content-Location 段体的位置(路径) Content-Base 段体的基位置 有的域除了值之外,还带有参数。值与参数、参数与参数之间以“;”分隔。参数名与参数值之间以“=”分隔。
1.MIME-Version: 表示使用的MIME的版本号,一般是1.0;如:MIME-Version: 1.0
2.Content-Type: 定义了正文的类型(“主类型/子类型”的形式),我们实际上是通过这个标识来知道正文内是什么类型的文件。比如text/plain 表示的是无格式的文本正文,text/html 表示的 Html 文档,image/gif 表示的是 gif 格式的图片等等。
主类型有text, image, audio, video, application, multipart, message等,分别表示文本、图片、音频、视频、应用、分段、消息等。
子类型:每个主类型都可能有多个子类型,如text类型就包含plain, html, xml, css等子类型
Tips:以X-开头的主类型和子类型,同样表示自定义的类型,未向IANA正式注册,但大多已经约定成俗了。如application/x-zip-compressed是ZIP文件类型。
Tips:在Windows中注册表的”HKEY_CLASSES_ROOT/MIME/Database/Content Type”内列举了除multipart之外大部分已知的Content-Type。
3.关于参数的形式,RFC里有很多补充规定,有的允许带几个参数,较为常见的有:
1 2 3 4 5 主类型 参数名 含义 text charset 字符集 image name 名称 application name 名称 multipart boundary 边界
4.multipart类型:邮件中常用到的复合类型,该类型表示正文是由多个部分组成的,后面的子类型说明的是这些部分之间的关系。 邮件中用到的三个类型有:
(1).multipart/alternative:表示正文由两个部分组成,可以选择其中的任意一个。主要作用是在征文同时有text格式和html格式时,可以在两个正文中选择一个来显示,支持 html 格式的邮件客户端软件一般会显示其 HTML 正文,而不支持的则会显示其Text正文;
(2).multipart/related:表示文档的多个部分是相关的,一般用来描述 Html 正文与其相关的图片。
(3).multipart/mixed:表示文档的多个部分是混合的,指正文与附件的关系。如果邮件的MIME类型是multipart/mixed,即表示邮件带有附件。
Tips:multipart类型是MIME邮件的精髓。邮件体被分为多个段,每个段又包含段头和段体两部分,这两部分之间也以空行分隔。 它们之间的层次关系可归纳为下图所示:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +------------------------- multipart/mixed ----------------------------+ | | | +----------------- multipart/related ------------------+ | | | | | | | +----- multipart/alternative ------+ +----------+ | +------+ | | | | | | 内嵌资源 | | | 附件 | | | | | +------------+ +------------+ | +----------+ | +------+ | | | | | 纯文本正文 | | 超文本正文 | | | | | | | +------------+ +------------+ | +----------+ | +------+ | | | | | | 内嵌资源 | | | 附件 | | | | +----------------------------------+ +----------+ | +------+ | | | | | | +------------------------------------------------------+ | | | +----------------------------------------------------------------------+
补充总结:从层次关系中可以看出,如果在邮件中要添加附件必须定义multipart/mixed段;如果存在内嵌资源至少要定义multipart/related段;如果纯文本与超文本共存至少要定义multipart/alternative段。
什么是“至少”? 答:如果只有纯文本与超文本正文,那么在邮件头中将类型扩大化,定义为multipart/related,甚至multipart/mixed,都是允许的。 multipart诸类型的共同特征是,在段头指定“boundary”参数字符串,段体内的每个子段以此串定界。所有的子段都以--+boundary行开始,父段则以--+boundary+--行结束
。段与段之间也以空行分隔。在邮件体是multipart类型的情况下,邮件体的开始部分(第一个“–” +boundary行之前)可以有一些附加的文本行,相当于注释,解码时应忽略。段间也可以有一些附加的文本行,不会显示出来。
些复合类型又是可以嵌套使用的,比如说一个带有附件的邮件,同时有html与text两种格式的正文,则邮件的结构是:1 2 3 4 5 6 7 8 9 Content-Type: multipart/mixed 部分一: Content Type : multipart/alternative: Text 正文; Html 格式的正文 部分二: 附件 邮件结束符;
由于复合类型由多个部分组成,因此需要一个分隔符来分隔这多个部分,这就是上面的邮件源文件中的boundary所描述的,对于每一个Contect type :multipart/* 的内容,都会有这么一个说明,表示多个部分之间的分隔。
含有 MIME/BASE64编码的邮件,你查看它的源码时一般都含有:“This is a multi-part message in MIME format.
”这样的句子。也可以被绝大多数的email程序进行解码,包括Netscape、MS Mail、Eudora等。这些程序可以正确识别邮件的正文,恢 MIME/BASE64 编码的部分为正确的文字或夹带的二进制文件。
3.邮件原始信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 Received: from 发信邮件服务器 (unknown [115.124.23.89]) by 收信邮件服务器 (NewMx) with SMTP id for <收信人>; Fri, 05 Jun 2020 10:56:48 +0800 X-QQ-FEAT: Nrj7KsauvmTPZQ**********Du8ZT3+WQp6dDEfYY= X-QQ-MAILINFO: Nj4k/6lmmRCSyBQ**********/EB9mnO1izsI+o1HWNTKN+0+IWP X-QQ-mid: mxsza48t时间戳-收信时间tfqsxubj9 X-QQ-ORGSender: system@notice.aliyun.com X-QQ-XMAILINFO: NlBzqDjet/+1*********/lm+frv3kDP7DnqqWKEAAiP9c DKIM-Signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=newsletter.aliyun.com; s=s1024; t=时间戳-收信时间; h=Date:From:To:Message-ID:Subject:MIME-Version:Content-Type; bh=wfxerPzsEEmDRDRXjMJBI13r4XrE/SOsQJOnJYilKV8=; b=LwKBc0owsr5zgBDFwwpZl3DadzCmLooo17myWuCBjkfWRUGVPXnQ7w2idnLigyemJe0nXeZC4CVzxRUhXBbqy+8piGBYOgBTCs+TLAorsbek0DbnFmY/5psSHTzAAbXWBmKiPhklr0FYNcD7HTpXxHi6/4meSe5rNmJRGiDmPh8= X-EnvId: 106163880073 Received: from mscchannel011175060107.eu13(mailfrom:system@notice.aliyun.com fp:SMTPD_----.Wrr2.i) by smtp.aliyun-inc.com(127.0.0.1); Fri, 05 Jun 2020 10:56:47 +0800 Date: Fri, 5 Jun 2020 10:56:47 +0800 (CST) From: =?UTF-8?B?6Zi/6YeM5LqR?= <system@notice.aliyun.com> To: 收信人 Message-ID: 2710120060500404702 Subject: =?UTF-8?B?54mp6IGU572R5bmz5Y+w5bey5pSv5oyBU0RL5Zyo57q/6KOB5Ymq?= MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_2824562_163938721.时间戳-收信时间439" ------=_Part_2824562_163938721.时间戳-收信时间439 Content-Type: text/html;charset=utf-8 Content-Transfer-Encoding: quoted-printable <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.= w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" ><html xmlns=3D"http://www.w3.org/1999/xhtml" > =09<head> =09=09<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf= -8" />=09=09<title>=E9=98=BF=E9=87=8C=E4=BA=91=E5=AE=98=E7=BD=91=E7=BB=9F=E4=B8= =80=E4=B8=9A=E5=8A=A1=E9=82=AE=E4=BB=B6=E6=A0=B7=E5=BC=8F(=E4=B8=AD=E6=96= =87_=E4=B8=AD=E5=9B=BD=E7=AB=99)</title> =09</head> =09<img src=3D"http://ac.mmstat.com/aliyun.12.1?logtype=3D4&type=3Demail&ms= gid=3D2710120060500404702&areaid=3Dcn&siteId=3Dcn" /></body></html> ------=_Part_2824562_163938721.时间戳-收信时间439--
0x01 易邮局域网邮件服务器 环境说明:1 2 3 - Windows 7 - 服务端:eyoumailserversetup.exe - 客户端:foxMail
安装流程: Step1.运行邮件服务器软件通常一直下一步即可安装完成,完成后打开C:\EyouMailServer\MailServer.exe
界面如下:
weiyigeek.top-步骤1
Step2.工具->系统设置->邮箱域名设置(weiyigeek.top)
weiyigeek.top-步骤2
Step3.添加新账号设置并设置密码,利用foxmail进行添加账号登陆;
weiyigeek.top-步骤3
0x02 邮件服务测试 1.Telnet 测试搭建的邮件服务:
3.javamail.jar 描述:老规矩我们首先需要将mail包导入到工程之中,此处采用javamail-1.4.4
版本和类型;
基础示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package top.weiyigeek.email;import java.util.Properties;import javax.mail.Authenticator;import javax.mail.Message;import javax.mail.Message.RecipientType;import javax.mail.MessagingException;import javax.mail.PasswordAuthentication;import javax.mail.Session;import javax.mail.Transport;import javax.mail.internet.AddressException;import javax.mail.internet.InternetAddress;import javax.mail.internet.MimeMessage;import org.junit.Test;public class emailDemo { @Test public void sendMail () throws AddressException, MessagingException { Properties props = new Properties(); props.setProperty("mail.host" , "10.20.172.110" ); props.setProperty("mail.smtp.auth" ,"true" ); Authenticator authenticator = new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication () { return new PasswordAuthentication("admin@weiyigeek.top" , "weiyigeek" ); } }; Session session = Session.getDefaultInstance(props, authenticator); Message message = new MimeMessage(session); message.setFrom(new InternetAddress("admin@weiyigeek.top" )); message.setRecipient(RecipientType.TO, new InternetAddress("zw@weiyigeek.top" )); message.setSubject("Java-Mail测试邮件信息...." ); message.setContent("<h1 style='color:red'>测试发信信息</h1>" , "text/html;charset=UTF-8" ); Transport.send(message); } }
执行结果:(内部测试)1 2 3 [2020-06-11 18:30:40] SMTP 10.20.172.103 ESMTP authentication: admin@weiyigeek.top [2020-06-11 18:30:40] SMTP 10.20.172.103 mail from admin@weiyigeek.top to zw@weiyigeek.top successfully [2020-06-11 18:30:59] SMTP MailUndeliverable mail from admin to admin@weiyigeek.top successfully
weiyigeek.top-测试
实际案例: 利用了RedisPool+javamail实现用户发信与激活;
邮件激活发信页面:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <title>Email邮箱验证测试</title> <meta charset="utf-8" > <meta name="viewport" content="width=device-width, initial-scale=1" > <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" > <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> <script type="text/javascript" > $(function(){ $("#getCode" ).click(function(){ var email = $("#email" ).val(); console.log(email); $.get("/Web/UserOper?method=activeEmail&email=" +email,function(data,status){ console.info(data); }) }); }); </script> </head> <body> <div class ="container" id="activeemail" > <h1> 邮箱激活 </h1> <form class ="form-horizontal" role="form" action="UserOper?method=vertyEmail" > <label lass="col-sm-2 control-label" for="email">邮箱地址:</label> <div class ="form-group" > <div class ="col-sm-6" > <input type="text" class ="form-control" id="email" value="zw@weiyigeek.top" disabled> </div> </div> <label lass="col-sm-2 control-label" for="emailcode">邮箱验证码:</label> <div class ="form-group" > <div class ="col-sm-4" > <input type="text" class ="form-control" id="emailcode" placeholder="请输入验证码" name="emailcode" > </div> <div class ="col-sm-2" > <input type="button" class ="btn btn-default" value="获取验证码" id="getCode" > </div> </div> <button type="submit" class="btn btn-primary">提交验证</button> </form> </div> </body> </html>
请求部分servlet方法(采用反射进行实现):1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 /Web/src/top/weiyigeek/baseservlet/BaseServlet.java public class BaseServlet extends HttpServlet { @SuppressWarnings ("unused" ) @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("BaseServlet - 被继承的基础Servlet中的Service方法" ); String method = req.getParameter("method" ); String redirection = null ; Class<? extends BaseServlet> clazz = this .getClass(); try { Method md = clazz.getMethod(method, HttpServletRequest.class, HttpServletResponse.class); if (null != md) { redirection = (String) md.invoke(this , req, resp); } if (null != redirection){ req.getRequestDispatcher(redirection).forward(req, resp); } }catch (Exception e) { e.printStackTrace(); } } } /Web/src/top/weiyigeek/baseservlet/UserOper.java public class UserOper extends BaseServlet { private static final long serialVersionUID = 1L ; public UserOper () { super (); System.out.println("UserOper Servlet 构造方法!" ); } public String activeEmail (HttpServletRequest request,HttpServletResponse response) throws ServerException { System.out.println("邮件发信" ); return "/SendMail" ; } }
注册邮件发送servlet:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package top.weiyigeek.baseservlet;import java.io.IOException;import java.util.Random;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import redis.clients.jedis.Jedis;import top.weiyigeek.connredis.RedisDemo3;import top.weiyigeek.email.Email;import top.weiyigeek.utils.RedisPoolUtil;public class SendMail extends HttpServlet { private static final long serialVersionUID = 1L ; protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json;charset=utf-8" ); Jedis jedis = RedisPoolUtil.getJedis(); HttpSession session = request.getSession(); String sessionid = session.getId(); String email = request.getParameter("email" ); String emailcode = String.format("%04d" , new Random().nextInt(10000 )); String context=" <h3 style='color:grade'>欢迎注册WeiyiGeek博客</h3><br><b>邮箱激活验证码:" +emailcode+"</b>" ; if (!RedisDemo3.exits(jedis, sessionid) && !RedisDemo3.exits(jedis, email)) { System.out.println("session:" +sessionid+" emailcode:" +emailcode); try { RedisDemo3.setKeyString(jedis, sessionid, emailcode, 360 ); RedisDemo3.setKey(jedis, email, "false" ); try { Email.sendMail(email, "[注册码] weiyigeek Blog's" , context); response.getWriter().append("{\"status\":\"successful\",\"msg\":\"发信成功\"}" ); } catch (Exception e) { response.getWriter().append("{\"status\":\"Error\",\"msg\":\"发信失败,请重试!\"}" ); } } catch (Exception e) { e.printStackTrace(); jedis.close(); } }else if (!RedisDemo3.exits(jedis, sessionid) || !RedisDemo3.getKeyString(jedis, email).equals("true" )) { System.out.println("session:" +sessionid+"--激活码:" +emailcode); try { Email.sendMail(email, "[注册码] weiyigeek Blog's" , context); response.getWriter().append("{\"status\":\"successful\",\"msg\":\"再次发信成功\"}" ); } catch (Exception e) { response.getWriter().append("{\"status\":\"Error\",\"msg\":\"再次发信失败,请重试!\"}" ); } } else { System.out.println("session:" +sessionid); response.getWriter().append("{\"status\":\"error\",\"msg\":\"mail already active and verity!\"}" ); } jedis.close(); } }
执行结果:1 2 3 邮件发信 当前Redis连接池被使用的数量: 1 session:0327E936C1BB849C79410E5BEA3D55ED emailcode:8537
weiyigeek.top-示例