Email

mail

# 一、邮件协议

常用的电子邮件协议有SMTP、POP3、IMAP4,它们都隶属于TCP/IP协议簇。

默认状态下,分别通过TCP端口25、110和143建立连接。

# SMTP协议

SMTP的全称是 “Simple Mail Transfer Protocol”,即简单邮件传输协议。

它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式。

它的一个重要特点是它能够在传送中接力传送邮件,即邮件可以通过不同网络上的主机接力式传送。

SMTP认证,简单地说就是要求必须在提供了账户名和密码之后才可以登录SMTP服务器,这就使得那些垃圾邮件的散播者无可乘之机。增加SMTP认证的目的是为了使用户避免受到垃圾邮件的侵扰。

SMTP已是事实上的E-Mail传输的标准。

# POP协议

POP3(Post Office Protocol 3)即邮局协议的第3个版本,是因特网电子邮件的第一个离线协议标准。

POP邮局协议负责从邮件服务器中检索电子邮件。

它要求邮件服务器完成下面几种任务之一:

  • 从邮件服务器中检索邮件并从服务器中删除这个邮件;
  • 从邮件服务器中检索邮件但不删除它;
  • 不检索邮件,只是询问是否有新邮件到达。

POP协议支持多用户互联网邮件扩展,后者允许用户在电子邮件上附带二进制文件,如文字处理文件和电子表格文件等,实际上这样就可以传输任何格式的文件了,包括图片和声音文件等。在用户阅读邮件时,POP命令所有的邮件信息立即下载到用户的计算机上,不在服务器上保留。

# IMAP协议

互联网信息访问协议(IMAP)是一种优于POP的新协议。和POP一样,IMAP也能下载邮件、从服务器中删除邮件或询问是否有新邮件,但IMAP克服了POP的一些缺点。

例如,它可以决定客户机请求邮件服务器提交所收到邮件的方式,请求邮件服务器只下载所选中的邮件而不是全部邮件。客户机可先阅读邮件信息的标题和发送者的名字再决定是否下载这个邮件。

通过用户的客户机电子邮件程序,IMAP可让用户在服务器上创建并管理邮件文件夹或邮箱、删除邮件、查询某封信的一部分或全部内容,完成所有这些工作时都不需要把邮件从服务器下载到用户的个人计算机上。

支持IMAP的常用邮件客户端有:ThunderMail,Foxmail,Microsoft Outlook等。

# 二、腾讯企业邮箱

# 开通域名邮箱

# 注册企业微信

# Springboot整合腾讯企业邮箱

引入spring-boot-starter-mail就可以轻松完成邮件的发送。JavaMailSender类提供了强大的邮件发送能力,支持各种类型的邮件发送。

# 阿里云域名DNS解析

1、设置域名解析

登录阿里云账号,找到域名解析;

添加结果如下:

2、解析第三方邮件客户端

# 腾讯企业邮箱设置

1、获取授权码

2、开启SMTP协议

# JAVA代码

Maven依赖

<!-- ********  邮件依赖 Begin  ********** -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<!-- 发送html模板邮件 -->
<dependency>
    <groupId>net.sourceforge.nekohtml</groupId>
    <artifactId>nekohtml</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--********  邮件依赖 End  **********-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

application.yml

base:
  config:
    email:
      # 发送模板邮件,模板中使用logo图标
      companyLogoUrl: https://www.xxx.cn/img/home.png
      fromName: 发件人中文昵称
      username: admin@caochaochao.cn
      password: xxxKjxoGoU2xxxx
      
# 腾讯企业邮箱(smtp.exmail.qq.com)、阿里云邮箱(smtp.aliyun.com)
spring:
  # 发送腾讯企业邮箱邮件配置
  mail:
    host: smtp.exmail.qq.com
    username: ${base.config.email.username}
    # 邮箱授权码
    password: ${base.config.email.password}
    protocol: smtp
    port: 465
    # 默认的邮件编码为UTF-8
    default-encoding: UTF-8
    # 配置以SSL的方式发送, 这个需要端口是465
    properties:
      mail:
        smtp:
          auth: true
          ssl:
            enable: true
            trust: ${spring.mail.host}
        # 开启开发模式,打印详细日志
#        debug: true

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

实现类

@Component
public class EmailSend {
    protected static final Logger logger = LoggerFactory.getLogger(EmailSend.class);

    @Autowired
    private JavaMailSender javaMailSender;

    /**
     * 发送普通邮件【异步调用】
     *
     * @param from:    发送人
     * @param to:      接收人
     * @param subject: 主体
     * @param body:    文本内容
     * @author superC
     * @date 2020/4/6 17:40
     */
    @Async
    public void sendEmail(String from, String to, String subject, String body) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from);
        message.setTo(to);
        message.setSubject(subject);
        message.setText(body);
        javaMailSender.send(message);
        logger.info("【发送普通邮件】成功发送邮件至: {}", to);
    }

    /**
     * 发送HTML模板邮件【异步调用】
     *
     * @param from:    发件人邮箱
     * @param fromName:    发件人昵称
     * @param to:      收件人邮箱
     * @param subject: 标题
     * @param body:    内容
     * @author superC
     * @date 2020/4/6 17:41
     */
    @Async
    public void sendTemplateEmail(String from, String fromName, String to, String subject, String body) {
        MimeMessage message = javaMailSender.createMimeMessage();
        try {
            // 借助Helper类
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            if (fromName == null || "".equals(from)) {
                // 发件人邮箱,与配置文件中保持一致,所以直接从配置文件绑定过来了
                helper.setFrom(from);
            } else {
                // 这个发件人也可以设置为中文名称, 如下代码
                helper.setFrom(new InternetAddress(fromName + "<" + from + ">"));
            }
            // 收件人
            helper.setTo(to);
            // 标题
            helper.setSubject(subject);
            // 内容, 第一个参数为邮箱内容, 第二个参数为是否启用html格式,
            // 如果开启, 那么第一个参数如果有html标签, 会自动识别, 邮件样式更好看
            helper.setText(body, true);
            javaMailSender.send(message);
            logger.info("【发送HTML模板邮件】成功发送邮件至: {}", to);
        } catch (Exception e) {
            logger.error("【捕获异常-发送HTML模板邮件异常】\r\n异常记录:", e);
        }
    }

    /**
     * 发送HTML模板邮件,携带附件【异步调用】
     *
     * @param from:    发件人邮箱
     * @param fromName:    发件人昵称
     * @param to:      收件人邮箱
     * @param subject: 标题
     * @param body:    内容,必填项
     * @param files:   附件列表
     * @author superC
     * @date 2023/12/20 22:16
     */
    @Async
    public void sendTemplateEmailAddAtt(String from, String fromName, String to, String subject, String body, List<String> files) {
        MimeMessage message = javaMailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            if (fromName == null || "".equals(from)) {
                // 发件人邮箱,与配置文件中保持一致,所以直接从配置文件绑定过来了
                helper.setFrom(from);
            } else {
                // 这个发件人也可以设置为中文名称, 如下代码
                helper.setFrom(new InternetAddress(fromName + "<" + from + ">"));
            }
            // 收件人
            helper.setTo(to);
            // 标题
            helper.setSubject(subject);
            // 内容, 第一个参数为邮箱内容, 第二个参数为是否启用html格式,
            // 如果开启, 那么第一个参数如果有html标签, 会自动识别, 邮件样式更好看
            // 注意:带附件发送,邮件内容也不能为空
            helper.setText(body, true);
            // 附件
            for (String filePath : files) {
                FileSystemResource file = new FileSystemResource(filePath);
                // File.separator相当于\,是默认的系统文件分隔符,可以防止切换系统路径出错
                helper.addAttachment(
                        filePath.substring(filePath.lastIndexOf(File.separator)), file
                );
            }

            javaMailSender.send(message);
            logger.info("【发送HTML模板邮件】成功发送邮件至: {}", to);
        } catch (Exception e) {
            logger.error("【捕获异常-发送HTML模板邮件异常】\r\n异常记录:", e);
        }
    }

}
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

# 三、Pmail

个人邮件服务器

用一句话介绍该项目就是:“Private EMail Server”

Github地址:https://github.com/Jinnrry/PMail (opens new window)

PMail 在 GitHub 有 500+ Star,PMail 是一个追求极简部署流程、极致资源占用的个人域名邮箱服务器。

单文件运行,包含完整的收发邮件服务和 Web 端邮件管理功能。

只需一台服务器、一个域名、一行代码、一分钟部署时间,你就能够搭建出一个自己的域名邮箱。

# 项目优势

# 1、部署简单

使用Go语言编写,支持跨平台,编译后单文件运行,单文件包含完整的前后端代码。修改配置文件,运行即可。

# 2、资源占用极小

编译后二进制文件仅15MB,运行过程中占用内存10M以内。

# 3、安全方面

支持dkim、spf校验。正确配置的情况下,Email Test得分10分。

# 4、自动SSL证书

实现了ACME协议,程序将自动获取并更新Let’s Encrypt证书。

默认情况下,会为web后台也生成ssl证书,让后台使用https访问,如果你有自己的网关层,不需要https的话,在配置文件中将httpsEnabled 设置为2,这样管理后台就不会使用https协议。( 注意:即使你不需要https,也请保证ssl证书文件路径正确,http协议虽然不使用证书了,但是smtp协议还需要证书)。

# 5、邮件客户端支持

只要支持pop3、smtp协议的邮件客户端均可使用。

# 项目的不足

1、目前只完成了最核心的收发邮件功能。基本上仅针对单人使用,没有处理多人使用、权限管理相关问题。

2、前端 UI 交互较差。

# 项目架构

1、前端: vue3+element-plus

前端代码位于fe目录中,运行参考fe目录中的README文件

2、后端: golang + mysql

后端代码进入server文件夹,运行main.go文件

# 第三方邮件客户端配置

POP3地址: pop.[你的域名]

POP3端口: 110/995(SSL)

SMTP地址: smtp.[你的域名]

SMTP端口: 25/465(SSL)

# 后端接口文档

参见Wiki (opens new window)

# Docker安装Pmail

可以从作者的 realease 页面下载各个客户端支持 linux 和 Windows。

下载地址:https://github.com/Jinnrry/PMail/releases (opens new window)

也可以使用 docker 的方式进行安装。

# 准备工作

  • 服务器
  • 域名
  • docker-compose

如果服务器80,443端口没有被占用,搭建过程则较为简单。如果80,443端口被其他应用占用,搭建过程则较为繁琐。

# 启动容器

1 下载镜像

docker pull ghcr.io/jinnrry/pmail:latest
1

2 编写docker-compose.yml

mkdir -p /usr/local/docker/pmail
cd /usr/local/docker/pmail
vi docker-compose.yml
1
2
3

docker-compose.yml

version: '3.3'
services:
    pmail:
        restart: always
        image: 'ghcr.io/jinnrry/pmail:latest'
        container_name: pmail
        ports:
            - '465:465'
            - '25:25'
            - '443:443'
            - '80:80'
        volumes:
            - './config:/work/config'

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 阿里云配置

  1. 开放端口

    port

# pmail配置

容器启动成功后,浏览器访问公网 IP 即可进入引导页面。

  1. 数据库设置:默认即可。

  2. 密码设置:设置pmail管理员密码:admin/123456

  3. 域名设置:

    1. SMTP域名地址:输入你的主域名,例superc.cn
    2. Web域名地址:mail.super.cn
  4. DNS设置:将以下信息添加到dns解析处,阿里云域名解析如下图:

    dns

  5. 配置ssl:选择默认自动配置ssl证书,下一步完成之后就可以登录了。

    pmail 自动帮我们申请了 ssl 证书。

# Pmail使用

# 测试发送

send

# 微信推送

打开运行目录下的 config/config.json文件,编辑 weChatPush 开头的几个配置项,重启服务即可。

# Telegram推送

BotFather (opens new window) 创建并获取令牌机器人。 打开运行目录下的 config/config.json 文件,编辑 tg 开头的几个配置项,重启服务即可。

配置文件说明

{
  "logLevel": "info", //日志输出级别
  "domain": "domain.com", // 你的域名
  "webDomain": "mail.domain.com", // web域名
  "dkimPrivateKeyPath": "config/dkim/dkim.priv", // dkim 私钥地址
  "sslType": "0", // ssl证书更新模式,0自动,1手动
  "SSLPrivateKeyPath": "config/ssl/private.key", // ssl 证书地址
  "SSLPublicKeyPath": "config/ssl/public.crt", // ssl 证书地址
  "dbDSN": "./config/pmail.db", // 数据库连接DSN
  "dbType": "sqlite", //数据库类型,支持sqlite 和 mysql
  "httpsEnabled": 0, // web后台是否启用https 0默认(启用),1启用,2不启用
  "spamFilterLevel": 0,// 垃圾邮件过滤级别,0不过滤、1 spf dkim 校验均失败时过滤,2 spf校验不通过时过滤
  "httpPort": 80, // http 端口 . 默认 80
  "httpsPort": 443, // https 端口 . 默认 443
  "weChatPushAppId": "", // 微信推送appid
  "weChatPushSecret": "", // 微信推送秘钥
  "weChatPushTemplateId": "", // 微信推送模板id
  "weChatPushUserId": "", // 微信推送用户id
  "tgChatId": "", // telegram 推送chatid
  "tgBotToken": "", // telegram 推送 token
  "isInit": true // 为false的时候会进入安装引导流程 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22