C#使用System.Net邮件发送功能踩过的坑
作者:JerryMouseLi 发布时间:2022-09-09 18:16:11
1.EazyEmail邮件发送类库
Net 类库自带了邮件发送功能。笔者对该类库,从使用的角度进行了二次封装,nuget上可搜索EazyEmail,注入容器时通过委托来获得邮箱服务器的配置地址以及发送地址直接调用send方法即可。
容器注入代码。这里定义的委托,每次发送之前可以去数据库拿邮箱配置数据跟发送账户,笔者自己用的时候是通过Redis缓存 存取数据,因为像断网断电这种可能是批量出现的,需要批量发送告警邮件,所以放Redis里,然后Redis通过rdb功能设置每秒每个键变化就持久化的策略,没毛病。
services.AddEmailKit(() =>
{
EmailConfig emailConfig = new EmailConfig( );
#region 163网易邮件发送
emailConfig.EmailSmtpAddress = "smtp.163.com";
emailConfig.EmalHostPort = 587;
emailConfig.SendEmailAccount = "13737732703@163.com";
emailConfig.SendEmailPassWord = "******";
#endregion
#region qq 邮件发送
// emailConfig.EmailSmtpAddress = "smtp.qq.com";
// emailConfig.EmalHostPort = 587;
// emailConfig.SendEmailAccount = "87888397@qq.com";
// emailConfig.SendEmailPassWord = "*****";
#endregion
return emailConfig;
});
发送代码
MailBox QqMailbox = new MailBox();
QqMailbox.To = "87888397@qq.com";
QqMailbox.Body = "qqfadsfa邮箱测试";
QqMailbox.Cc = "935467953@qq.com";
QqMailbox.Subject = "qq邮fadfa箱测试";
emailQueueService.Enqueue(QqMailbox);
EazyEmail 内置阻塞队列,只要队列有邮件,里面开了一个线程会不断地发送,发送完毕会阻塞住,对应线程执行权也会回归线程池,一旦继续有邮件,线程自动唤醒会继续发送邮件。有关EazyEmail的使用与设计思路有需要介绍可留言,可另起一篇作讲解,已经上传到nuget,可自行搜索EazyEmail去使用,使用非常方便。
EazyEmail类库源码 github地址需要者可自行下载
2.邮件发送授权码与邮件密码
第三方客户端登录邮件服务器来进行发送邮件,接收邮件已经极为普遍,某种场景下是代码里嵌入发送邮件信息,当然也包含了发送邮件的密码,近两年邮件服务商为了提高邮件的保密性,网易与qq邮箱规定了第三方客户端发送邮件只能通过发送授权码。
网易发送授权码生成过程:
开启所需要的邮件发送服务跟接收服务
手机微信扫描发送二维码
手机短信发送之后,点击我已发送 生成授权码
此授权码可直接用来作为应用程序的发送密码。
qq邮箱发送授权码生成过程:
生成授权码步骤,设置,账户往下拉。
点击生成授权码,短信发送,我已发送,即可生成对应授权码。
备注:qq邮箱多年之前已经采用授权码方式,而网易,笔者在15年时测试第三方客户端是可以用密码发送的,当然现在15年设置开启了pop/smtp,或者imap/smtp服务,当时没有生成授权码的依然能用密码发送,只不过当你生成过授权码之后就在网易服务商这里就再也不能用密码发送了,第三方只能通过授权码发送。即便你删除完授权码,那么pop/smtp,或者imap/smtp服务就会自动关闭。
3.通过邮件密码来发送邮件
你是否同时会有这样的疑问,能否通过邮箱的密码来发送邮件呢?笔者之所以有如下思考,是基于用户的使用方便程度来考虑:
用户没有授权码的概念;
使用简便的角度来看,直接账户,登录密码是最方便的;
一开始,笔者心里也没有答案,但是想到,公司的邮箱密码是可以记录到foxmail,然后通过这个客户端来进行邮件的发送与接收管理邮箱。但是我直接用代码来发送邮件却不成功,报失败。失败代码如下:
static void Main(string[] args)
{
try
{
var client = new SmtpClient
{
DeliveryMethod = SmtpDeliveryMethod.Network,
EnableSsl = true,
Host = "smtp.lead-it.cn",
Port = 465
};
client.Credentials = new NetworkCredential("hekun@lead-it.cn", "*********");
MailMessage msg = new MailMessage("hekun@lead-it.cn", "87888397@qq.com", "测试", "邮箱测试");
client.Send(msg);
Console.WriteLine("邮件已发送,请注意查收!");
Console.ReadKey();
}
catch (SmtpException ex)
{
Console.WriteLine("发送邮件失败:" + ex.Message);//输出错误信息
}
}
4.Wireshark抓包分析
遇到困难自然是迎难而上,foxmail能做到的事,我们一样能做到。只需要foxmail的发送邮件的过程抓包,一一分析,然后自己邮件发送过程,对比,找出差异就能定位问题。
抓了小半天包,没有结果,抓不到pop跟SMTP协议的包。
后面静下心来仔细分析是因为公司邮箱服务器(163企业邮箱服务器,管理员设置了ssl)加了ssl认证。
下面只能贴上163服务器不加密的发送过程与接收过程的wireshark抓包,忘记密码的同学可以自己抓包找回密码,仅限在不加密的情况下。
通过pop协议接收邮件。想了解IMAP协议的自行抓包,方法一样
smtp发送抓包如下,可以看到发送时用户名密码是加密的
5.通过密码SSL发送成功
先看下发送成功代码
static void Main(string[] args)
{
try
{
ServicePointManager.ServerCertificateValidationCallback = (s, cert, chain, errors) => true;
var client = new SmtpClient
{
DeliveryMethod = SmtpDeliveryMethod.Network,
EnableSsl = true,
Host = "smtp.lead-it.cn",
Port = 587
};
client.Credentials = new NetworkCredential("hekun@lead-it.cn", "********");
MailMessage msg = new MailMessage("hekun@lead-it.cn", "87888397@qq.com", "测试", "邮箱测试");
client.Send(msg);
Console.WriteLine("邮件已发送,请注意查收!");
Console.ReadKey();
}
catch (SmtpException ex)
{
Console.WriteLine("发送邮件失败:" + ex.Message);//输出错误信息
}
}
5.1 微软不支持在465的ssl
通过不断的搜索,与调试发现。
oschina上有这样一篇文章
Microsoft is not supporting SSL over port 465 in c# 4/.NET 4.
Microsoft only supports SSL on 587 through "STARTTLS".
大意是微软不支持SSL端口开在465,有可能465端口被微软的其他库占用。而一般邮件服务商会开多个ssl端口,比如587。当然如果是公司自己搭建的邮件服务器就需要注意这个坑了,你只开了465 ssl端口就意味着永远用不了微软爸爸的邮件库。
5.2 ssl证书
解决了上面的5.1,又有了5.2问题如下:
大概含义是ssl证书无效。
在stackoverflow上找到了答案:
the-remote-certificate-is-invalid
如果没有ssl证书,直接加入下面语句,返回true,有些信息就没加密。需要加密的读者自行搜索加入ssl文件证书。
ServicePointManager.ServerCertificateValidationCallback = (s, cert, chain, errors) => true;
公司企业邮箱(企业级的网易邮箱允许第三方客户端不通过授权码)通过邮件密码发送邮件到qq邮箱,qq邮箱收到邮件如下:
至此,问题解决。
6 小结
关于能用授权码还是密码发送邮件,无法由我们决定,由邮件服务商提供的接口决定,他没有授权码生成功能,自然只能通过密码发送;他(网易邮箱,QQ邮箱)规定只能用授权码发送,那我们也只能如此;如果是授权码密码两者都能用,读者自己在安全性与使用便捷性做考虑衡量决定。
来源:https://www.cnblogs.com/JerryMouseLi/p/13954114.html


猜你喜欢
- 前言MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。那
- 实现以下功能:验证字符串是否由正负号(+-)、数字、小数点构成,并且最多只有一个小数点验证字符串是否仅由[0-9]构成验证字符串是否由字母和
- 废话不多说了,一切尽在代码中,具体代码如下所示:界面<?xml version="1.0" encoding=&q
- 稀疏数组:当一个二维数组中大部份的值为0,或者为同一值的时候,可以用稀疏数组来保存实现思路:记录二维数组有多少行多少列、多少个不同的值把不同
- 本文实例为大家分享了Android实现聊天界面的具体代码,供大家参考,具体内容如下文件目录在app下的build.gradle中添加依赖库(
- 用Java代码模拟卖100张火车票问题:四个售票窗口同时售票且不能卖出同一张火车票。代码如下。package lesson;public c
- 这篇文章主要介绍了springmvc视图解析流程代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 一、增删改1、增加<!-- 添加用户--><insert id="saveUser" paramete
- 关于unicode和utf的关系,可以简单的记忆:Unicode是一个编码组织、一个编码规范、在java中指utf-16;utf是Unico
- 本文实例讲述了C#遍历系统进程的方法。分享给大家供大家参考。具体实现方法如下:建立一个listBox将进程名称遍历进去this.listBo
- 之前有个兄弟给我的卷一re了帖子,我当时没有g,m,直到他把它删掉才后悔莫及,人生最痛苦的事情莫过于此。。。。。。好,即便如此,我们还是满怀
- 预备知识Java中的线程对象是Thread,新建线程也只有通过创建Thread对象的实例来创建。先说结论1 Runnable没有返回值的Fu
- 前言由于刚写项目不久,在写 web 后台接口时,经常会对前端传入的参数进行一些规则校验,如果入参较少还好,一旦需要校验的参数比较多,那么使用
- 今天把Android Studio 2.3 更新为了3.0 遇到一个蛋疼的问题如图:格式化完代码后发现不会自动换行了,看着真心不爽。后来发现
- 之前已经为大家介绍过利用Java实现带GUI的气泡诗词特效,本文将为大家介绍另一种方法同样也可以实现气泡诗词的效果。下面是示例代码impor
- 入门Springboot项目创建在IDEA中创建即可。注意点:1、所有文件都需要放在 :Application文件的同级或下级目录中2、ap
- 线程调用类对象在前面的示例中,我们为线程任务使用了通常的函数。实际上,我们可以使用任何可调用对象或者lambda函数,如下调用类对象的例子:
- 1. 抽象类是什么️给大家上一篇小作文,看完这个,你就理解了什么叫做抽象类在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不
- 前提在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结。SpringMVC中处理控制器参
- 此问题的产生,主要是数据库的字段名一样导致三张表 DOCTOR JOB OBJECT有问题的查询语句和查询结果是:SELECT d.*,j.