# EmailSystem **Repository Path**: n70huihui/email-system ## Basic Information - **Project Name**: EmailSystem - **Description**: 湖南大学计算机网络课程设计——邮件系统项目后端代码,基于 SpringBoot + Netty - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-11-14 - **Last Updated**: 2024-12-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 项目说明 ## 写在前面 - 本项目为湖南大学计算机网络课程设计——邮件系统的后端代码,**前端代码仓库请[访问这里](https://gitee.com/hunan-university-g_0/computer-network-client-app.git)**。 - 这个项目我个人建议**尽早开始做**,需要开发的内容有很多,我们小组因为拖到最后两周才开始写,所以导致内容上做得比较粗糙,基本功能是实现了,但是还有很大提升空间。 - 实际上这个小班课的成绩是**比较成绩**,还是建议大家多做点课程要求之外的内容比价好,这样可以多加分。包括但不限于:发附件、和外部通信(这里的外部通信指的是和第三方邮件系统,如 QQ 系统通信)…… - 增加的功能最好围绕 “邮件” 这一内容展开,没必要在其他方面(如用户头像、用户工作组等功能)浪费时间。并且,我们最后验收的时候,老师实际上**不怎么看具体实现**,而是**看最终结果**。所以一些额外功能的实现可以用一些取巧的方式来:比如如果要实现附件功能,可以结合 OSS 来做,没必要强行按照报文内容解析;再者往第三方邮箱发邮件,可以不用自己买域名然后解析啥的,直接调用 Java Mail API 利用**自己的 qq 邮箱**往第三方邮箱发邮件也是可行的(能蒙混过关是最好,但是我不保证每个老师都能这样混过去)。 ## 项目概述 项目内容: 1. 基于 SMTP 的邮件发送服务器设计与实现。 2. 基于 POP3 的邮件接收服务器设计与实现。 3. 移动 Android 客户端平台设计与实现。 技术选型: - 前端:uni-app - 后端:SpringBoot + Netty(JDK 版本为 17) - 数据库:MySQL ## 项目结构 本项目由两个模块构成,分别是 common 模块和 server 模块,SMTP 服务器 和 POP3 服务器的实现并没有各自单独拿出去,而是耦合到了本项目中: - email-common:主要存放一些封装类,本包不编写业务逻辑。 - constant:存放各种常量封装类。 - context:存放上下文封装类。 - exception:存放异常封装类。 - pojo:存放项目用到的 DTO、VO 类。 - properties:存放配置属性相关的类。 - result:存放返回的结果封装类。 - utils:存放各种工具类。 - email-server:项目的主要业务逻辑在此包编写。 - config:SpringBoot 配置类。 - controller:控制层。 - entity:实体类,其中每一个实体对应数据库中的一张表。 - handler:异常处理器类。 - interceptor:拦截器类。 - mapper:DAO 层。 - pop3:POP3 服务器实现。 - service:服务层。 - smtp:SMTP 服务器实现。 ## 疑难分析 ### SMTP 服务实现 #### 用户发送邮件 本次课程设计其中一个要求就是让我们实现 SMTP 服务器,对于传统的邮件服务,分有邮件服务器以及用户代理。在本次项目中,用户代理就是我们的移动客户端。对于该用户代理,用户使用 http 请求携带邮件内容信息向后端发送发邮件的请求,后端接收后,调用 Java Mail 的 API 往我们自定义的 SMTP 服务器中发 SMTP 报文。 ![输入图片说明](https://foruda.gitee.com/images/1732375414234854226/2ef0041e_14204035.png "image-20241123160746259.png") ![输入图片说明](https://foruda.gitee.com/images/1732375455968915984/5d035d38_14204035.png "image-20241123160816674.png") ![输入图片说明](https://foruda.gitee.com/images/1732375476637781144/9cb60824_14204035.png "image-20241123160902286.png") #### SMTP 报文 调用 Java Mail 后,自定义的 SMTP 服务器会接收到 SMTP 报文,我们的任务就是要处理 SMTP 报文,下述所示便是 SMTP 报文的片段,红色部分是发送端发出的 SMTP 报文,绿色部分是 SMTP 服务器接收到报文之后给予的响应: ![输入图片说明](https://foruda.gitee.com/images/1732375513373426625/37cd9777_14204035.png "image-20241123161437622.png") ![输入图片说明](https://foruda.gitee.com/images/1732375530396994001/c5d418d0_14204035.png "image-20241123161446624.png") ![输入图片说明](https://foruda.gitee.com/images/1732375544225566309/08f5d4c8_14204035.png "image-20241123161559400.png") SMTP 报文的各种指令及其意义以及对应的响应内容如下: | **指令** | **含义** | **推荐响应内容** | | ------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | **HELO** | 客户端通过发送主机名向服务器表明身份(早期版本使用)。 | `250 Hello , pleased to meet you` | | **EHLO** | 扩展的 HELO,支持现代 SMTP 的扩展功能,例如认证、TLS 等。 | `250-Hello ` `250-AUTH PLAIN LOGIN` `250 STARTTLS` | | **MAIL FROM** | 表示新邮件的开始,指定邮件的发件人地址。 | `250 OK` | | **RCPT TO** | 指定邮件的接收者地址,可多次发送以表示多个接收者。 | `250 OK` 或 `550 No such user here`(如果用户不存在) | | **DATA** | 表示即将开始传输邮件正文。 | `354 Start mail input; end with .` | | **QUIT** | 客户端请求断开与服务器的连接。 | `221 closing connection` | | **RSET** | 重置会话,清除当前会话状态(如取消已指定的发件人和收件人信息)。 | `250 OK` | | **VRFY** | 验证给定的用户名或邮箱是否存在。 | `252 Cannot VRFY user`(安全原因通常不实现,直接返回无法验证的响应) | | **EXPN** | 展开给定的邮件列表地址并返回所有成员地址(类似于组邮件功能)。 | `502 Command not implemented`(通常禁用,防止信息泄露) | | **HELP** | 客户端请求服务器返回帮助信息。 | `214 Commands supported: HELO, EHLO, MAIL FROM, RCPT TO, DATA, QUIT` | | **NOOP** | 测试服务器连接是否正常,不执行其他操作。 | `250 OK` | | **STARTTLS** | 请求加密连接的升级(TLS 协议)。 | `220 Ready to start TLS` | | **AUTH** | 客户端请求进行认证(如登录用户名和密码)。 | `235 Authentication successful` 或 `535 Authentication credentials invalid` | | **BDAT** | 用于传输大块数据(在 ESMTP 中使用)。 | `250 OK` | | **TURN** | 反转当前 SMTP 连接的方向(客户端变成服务器,服务器变成客户端),已废弃。 | `502 Command not implemented` | #### SMTP 服务器实现 SMTP 服务器的实现涉及到网络通信,所以本次的实现采用 Netty 作为网络通信的开发框架。SMTP 服务的启动代码如下: ![输入图片说明](https://foruda.gitee.com/images/1732375566179537614/d944d904_14204035.png "image-20241123162054668.png") 在本次的服务器中,主要由 SmtpHandler 来处理 SMTP 报文中的各种指令: ![输入图片说明](https://foruda.gitee.com/images/1732375587461813851/22325321_14204035.png "image-20241123162317881.png") 每一个 SmtpHandler 中都会维护一个 Session 对象,用来存储通信过程中的重要数据(如发件人邮箱、收件人邮箱、邮件内容等)。在 channelRead0 中,我们需要根据传递过来的各种指令做出各种处理,为了防止写大量的 if-else 分支语句,这里采用了策略模式:SmtpHandler 中维护了一个 Map 集合,其 key 值为各种指令的字符串,而 value 值为处理对应指令的 CommanHandler 类,它们都继承了一个公共的抽象父类 CommandHandler,通过重写其 commandHandle 方法来让各个不同的子类具有处理不同命令的能力: ![输入图片说明](https://foruda.gitee.com/images/1732375605931911522/cdf497d4_14204035.png "image-20241123162702123.png") 之后,根据不同的指令让各个 CommandHandler 做出不同的操作即可。 最终实现的功能便是:当用户往 SMTP 服务器上发送邮件时,SMTP 服务器判断收件人的邮箱地址,如果是本地系统的域名,则直接存入数据库中;如果是第三方系统的域名,则进行邮件转发,转发到对应的第三方系统的 SMTP 服务器上。 ### POP3 服务实现 POP3 实现与 SMTP 类似,这里不贴代码了,可以自己在源代码的 `email-server` 模块中 `pop3` 包里查看。 #### 用户拉取邮件 与 SMTP 用于发送邮件类似,在 POP3 中,用户使用 http 请求向后端发送收邮件的请求,后端接收后,调用 Java Mail 的 API 往我们自定义的 POP3 服务器中发 POP3 报文,拉取邮件。为了让我们的拉取更加贴合 POP3 协议不为用户保存邮件的思想,我们拉取邮件后调用 API 发送 DELE 指令,让 POP3 服务器把邮件从服务器中删除。 #### POP3 报文 调用 Java Mail 后,自定义的 POP3 服务器会接收到 POP3 报文,我们的任务就是要处理 POP3 报文。 POP3 报文的各种指令及其意义以及对应的响应内容如下: | **指令** | **含义** | **推荐响应内容** | | -------- | ---------------------------------------------------------- | ------------------------------------------------------------ | | **USER** | 客户端提供用户名进行身份验证。 | `+OK User accepted` 或 `-ERR Invalid user` | | **PASS** | 客户端提供密码完成身份验证。 | `+OK Authentication successful` 或 `-ERR Authentication failed` | | **STAT** | 请求邮箱状态,返回邮件数量和总字节大小。 | `+OK ` | | **LIST** | 获取所有邮件的编号及大小,或单个邮件的大小。 | `+OK`(后续返回每封邮件的 ` ` 或 `-ERR` 错误消息) | | **RETR** | 请求下载指定邮件的内容。 | `+OK `(后续返回邮件内容,以 `.` 结束) | | **DELE** | 标记指定邮件为删除状态,服务器在会话结束时删除这些邮件。 | `+OK Message marked for deletion` 或 `-ERR No such message` | | **NOOP** | 测试服务器连接是否正常,不执行其他操作。 | `+OK` | | **RSET** | 取消当前会话中所有的删除标记。 | `+OK All delete marks cleared` | | **QUIT** | 客户端请求断开与服务器的连接,邮件的删除操作会在此时生效。 | `+OK signing off` | | **TOP** | 获取指定邮件的头部信息和若干行正文(非必需支持)。 | `+OK Top of message follows`(后续返回邮件头部信息和正文) | | **UIDL** | 请求返回每封邮件的唯一标识符,用于客户端记录邮件状态。 | `+OK`(后续返回每封邮件的 ` ` 或 `-ERR` 错误消息) | | **APOP** | 使用加密摘要验证身份(比 USER/PASS 更安全)。 | `+OK Authentication successful` 或 `-ERR Authentication failed` | | **CAPA** | 请求服务器的功能支持信息。 | `+OK Capability list follows`(后续返回支持的功能列表) | #### POP3 服务器实现 最终实现的功能是:用户调用接口拉取邮件,POP3 把数据库中的邮件读取出来返回给用户后,把邮件从数据库中删除。