通过 Exchange Online (Office 365) 使用 System.Net.Mail 发送 SMTP 电子邮件

Posted

技术标签:

【中文标题】通过 Exchange Online (Office 365) 使用 System.Net.Mail 发送 SMTP 电子邮件【英文标题】:Send SMTP email using System.Net.Mail via Exchange Online (Office 365) 【发布时间】:2011-09-08 20:05:47 【问题描述】:

我们正在测试新的 Office 365 测试版,我在 Exchange Online 服务上有一个邮件帐户。现在我正在尝试连接一个可以从我的测试帐户发送 smtp 电子邮件的 LOB 应用程序。

但是,Exchange 365 平台要求对端口 587 进行 TLS 加密,并且 System.Net.Mail 的“功能”不允许隐式 SSL 加密。

有没有人设法让 C# 通过这个平台发送邮件?

我有以下应该发送邮件的基本代码 - 任何建议都将不胜感激。

SmtpClient server = new SmtpClient("ServerAddress");
server.Port = 587;
server.EnableSsl = true;
server.Credentials = new System.Net.NetworkCredential("username@mydomain.com", "password");
server.Timeout = 5000;
server.UseDefaultCredentials = false;

MailMessage mail = new MailMessage();
mail.From = new MailAddress("recipent@anyaddress");
mail.To.Add("username@mydomain.com");
mail.Subject = "test out message sending";
mail.Body = "this is my message body";
mail.IsBodyhtml = true;

server.Send(mail);

【问题讨论】:

是的,你得到一个超时 - 这显然是默认失败 所以我开始怀疑这是否可能使用 C# 以及 System.Net.Mail 命名空间对 SSL 设置的限制。似乎 Exchange 在 Office 365 上公开了它的 web 服务,并且很多文章都在谈论利用它来处理邮箱等。但是我真的不想将整个邮箱公开给我的 LOB 应用程序。 SMTP 中的“S”代表“简单”——我简直不敢相信微软安装的微软邮件服务器不能使用微软的编程语言。请有人告诉我我在这里遗漏了一些东西! 我希望我有一台服务器可以试一试。所以,它的超时,这是一个问题。由于您的测试邮件是纯文本,如果您不说正文是HTML,它是否有效? 我把上面的代码改成了 mail.IsBodyHtml = false;但我仍然得到超时。我很感激这个建议——在这个时候我愿意尝试任何事情来让它发挥作用! 在这一点上,我会指责 MS 并说交易所不公平。邮件是否已发送并且它以某种方式期望您关闭连接?如果您将几条消息(例如 1 给自己)排成一行,这是否有效 - 交换人员是否能够看到任何显示的日志文件,例如连接,这可能是一个中继问题,因为您不允许这样做,但它需要连接并挂起(我的反垃圾邮件可以做到这一点)..? 【参考方案1】:

在 2020 年,这些代码似乎返回异常为

System.Net.Mail.SmtpStatusCode.MustIssueStartTlsFirstSMTP 服务器需要安全连接或客户端未通过身份验证。服务器响应为:5.7.57 SMTP;在 MAIL FROM 期间,客户端未通过身份验证发送匿名邮件

这段代码对我有用。

            using (SmtpClient client = new SmtpClient()
            
                Host = "smtp.office365.com",
                Port = 587,
                UseDefaultCredentials = false, // This require to be before setting Credentials property
                DeliveryMethod = SmtpDeliveryMethod.Network,
                Credentials = new NetworkCredential("alias@fulldomain.com", "password"), // you must give a full email address for authentication 
                TargetName = "STARTTLS/smtp.office365.com", // Set to avoid MustIssueStartTlsFirst exception
                EnableSsl = true // Set to avoid secure connection exception
            )
            

                MailMessage message = new MailMessage()
                
                    From = new MailAddress("alias@fulldomain.com"), // sender must be a full email address
                    Subject = subject,
                    IsBodyHtml = true,
                    Body = "<h1>Hello World</h1>",
                    BodyEncoding = System.Text.Encoding.UTF8,
                    SubjectEncoding = System.Text.Encoding.UTF8,

                ;
                var toAddresses = recipients.Split(',');
                foreach (var to in toAddresses)
                
                    message.To.Add(to.Trim());
                

                try
                
                    client.Send(message);
                
                catch (Exception ex)
                
                    Debug.WriteLine(ex.Message);
                
            

【讨论】:

这看起来很有希望,但我收到如下错误:System.Net.Mail.SmtpException:发送邮件失败。 ---> System.Net.WebException:无法连接到远程服务器 ---> System.Net.Sockets.SocketException:尝试以访问权限 52.97.133.146:587 禁止的方式访问套接字 ...有什么想法吗? 我还想知道在哪里可以找到“用于身份验证的完整电子邮件地址”。能详细点吗? "full email address for authentication" 是用于发送电子邮件的电子邮件帐户。例如,我公司的电子邮件是 my-name@my-company.com。我创建了另一封电子邮件来发送电子邮件,例如 noreply@my-company.com。我将在那里填写 noreply 帐户的凭据。 启用两因素身份验证时会怎样?【参考方案2】:

修正了上面工作代码中的一些错别字:

MailMessage msg = new MailMessage();
msg.To.Add(new MailAddress("someone@somedomain.com", "SomeOne"));
msg.From = new MailAddress("you@yourdomain.com", "You");
msg.Subject = "This is a Test Mail";
msg.Body = "This is a test message using Exchange OnLine";
msg.IsBodyHtml = true;

SmtpClient client = new SmtpClient();
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential("your user name", "your password");
client.Port = 587; // You can use Port 25 if 587 is blocked (mine is!)
client.Host = "smtp.office365.com";
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.EnableSsl = true;
try

    client.Send(msg);
    lblText.Text = "Message Sent Succesfully";

catch (Exception ex)

    lblText.Text = ex.ToString();

我有两个使用上述代码的 Web 应用程序,它们都可以正常工作。

【讨论】:

您运行的是哪个版本的 Office 365。我有一段时间没看这段代码了,但我们正准备从小型企业计划 P1 移植到企业计划 E1,我想知道我最初的问题是否是由于我们在 P1 计划中造成的。 很遗憾,此代码在 Office 365 中不再有效。出现的错误消息是 SMTP 服务器需要安全连接或客户端未通过身份验证。服务器响应为:5.7.57 SMTP;在 MAIL FROM [HE1PR05CA0133.eurprd05.prod.outlook.com] 期间,客户端未通过身份验证发送匿名邮件 是的,Simon Price 是对的,即使在端口 25 上它也不起作用。 我们一个组织在账户上启用了 MFA,那么我们如何配置呢? 这真是太棒了!谢谢!。【参考方案3】:

这里是一些可能正在搜索此线程以找到此问题的答案的人的附注。 (在实施此解决方案之前,请务必阅读底部的注意事项。)我在为我的 MS Office 365 订阅没有用户或域的客户发送电子邮件时遇到问题。我试图通过我的 Me@MyDomain.com 365 帐户发送 SMTP,但 .NET 邮件消息是从 Client@ClientDomain.com 发送的。这是为我弹出“5.7.1 客户端没有权限”错误的时候。为了补救,MailMessage 类需要将 Sender 属性设置为我提供的 SMTP 凭据在 O365 中有权“发送为”的电子邮件地址。我选择使用我的主帐户电子邮件 (Me@MyDomain.com),如下面的代码所示。请记住,我可以使用我的 O365 帐户有权“发送为”的任何电子邮件地址(即 Support@MyDomain.com、no-reply@MyDomain.com 等)

using System;
using System.Net.Mail;

namespace ConsoleApplication1

   class Program
   
      static void Main(string[] args)
      
         using (
            MailMessage message = new MailMessage
            
               To =  new MailAddress("Recipient1@Recipient1Domain.com", "Recipient 1") ,
               Sender = new MailAddress("Me@MyDomain.com", "Me"),
               From = new MailAddress("Client@ClientDomain.com", "Client"),
               Subject=".net Testing"
               Body="Testing .net emailing",
               IsBodyHtml=true,
            
         )
         
            using (
               SmtpClient smtp = new SmtpClient
               
                  Host = "smtp.office365.com",
                  Port = 587,
                  Credentials = new System.Net.NetworkCredential("Me@MyDomain.com", "Pa55w0rd"),
                  EnableSsl = true
               
            )
            
               try  smtp.Send(message); 
               catch (Exception excp)
               
                  Console.Write(excp.Message);
                  Console.ReadKey();
               
            
         
      
   

请注意,SmtpClient 只是一次性的,并且能够在 .NET Framework 4 中使用 Using 块 .NET Framework 2 到 3.5 的用户应该使用 SmtpClient...

SmtpClient smtp = new SmtpClient

   Host = "smtp.office365.com",
   Port = 587,
   Credentials = new System.Net.NetworkCredential("Me@MyDomain.com", "Pa55w0rd"),
   EnableSsl = true
;
try  smtp.Send(message); 
catch (Exception excp)

   Console.Write(excp.Message);
   Console.ReadKey();

生成的电子邮件标题将如下所示:

Authentication-Results: spf=none (sender IP is )  
   smtp.mailfrom=Me@MyDomain.com;  
Received: from MyPC (192.168.1.1) by  
   BLUPR13MB0036.namprd13.prod.outlook.com (10.161.123.150) with Microsoft SMTP  
   Server (TLS) id 15.1.318.9; Mon, 9 Nov 2015 16:06:58 +0000  
MIME-Version: 1.0  
From: Client <Client@ClientDomain.com>  
Sender: Me <Me@MyDomain.com>  
To: Recipient 1 <Recipient1@Recipient1Domain.com>  

-- 小心点-- 请注意,某些邮件客户端可能会将发件人地址显示为注释。例如,Outlook 将在阅读窗格的标题中显示以下内容:

代表客户

但是,只要收件人使用的电子邮件客户端不是完全垃圾,这不会影响回复地址。回复应仍使用发件人地址。为了覆盖您的所有基础,您还可以利用 MailMessage.ReplyToList 属性为客户端提供使用正确回复地址的每一个机会。

另外,请注意,某些电子邮件服务器可能会完全拒绝任何代表另一家公司发送的电子邮件,这些公司在网站上设置了域所有者政策限制。一定要彻底测试并寻找任何反弹。我可以告诉你,我的个人 Hotmail (mail.live.com) 电子邮件帐户会拒绝我代表我的某个客户发送的邮件,但其他客户可以通过。尽管我怀疑它与我客户的域 TXT“spf1”记录有关,但我没有回答为什么它会拒绝代表一个域而不是另一个域发送的电子邮件。也许知道的人可以对这个主题有所了解?

【讨论】:

请注意 - office 365 似乎不喜欢添加发件人标签【参考方案4】:

Office 365 使用两台服务器,smtp 服务器和保护扩展服务器。

第一个服务器是 smtp.office365.com(smtp 客户端的属性主机),第二个服务器是 STARTTLS/smtp.office365.com(smtp 客户端的属性 TargetName)。另一件事是必须在设置网络凭据之前设置 Usedefaultcredential =false。

    client.UseDefaultCredentials = False
    client.Credentials = New NetworkCredential("user@domain.com", "Password")
    client.Host = "smtp.office365.com"
    client.EnableSsl = true
    client.TargetName = "STARTTLS/smtp.office365.com"
    client.Port = 587

    client.Send(mail)

【讨论】:

【参考方案5】:

我已将用于对抗 smtp.google.com:587 的 c# 代码移植到通过 office365 工作但没有成功。在使用 Ssl 之前/之后尝试了所有 Credential 组合,以及几乎所有在 Internet 上提出的建议 - 没有成功(得到 5.7.1 .... 错误)。

最后终于在 .Send(message) 之前从某个地方得到了这条线

smtpClient.TargetName = "STARTTLS/smtp.office365.com";

从那时起 - 每一次 send() 都取得了巨大的成功。

【讨论】:

【参考方案6】:

终于成功了!

输入smtpClient.UseDefaultCredentials = false;smtpClient.Credentials = credentials;之后 然后问题解决了!

            SmtpClient smtpClient = new SmtpClient(smtpServerName);                          
            System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(smtpUName, smtpUNamePwd);

            smtpClient.Credentials = credentials;
            smtpClient.UseDefaultCredentials = false;  <-- Set This Line After Credentials

            smtpClient.Send(mailMsg);
            smtpClient = null;
            mailMsg.Dispose();

【讨论】:

-1;在设置Credentials = xxyy 后设置UseDefaultCredentials = false; 将您的凭据重置为null。所以你根本不发送任何凭据。 (是的,我知道我在 9 年后对这个答案进行了死灵法......)见***.com/questions/6656039/… 很明显,这与建议相矛盾。 UseDefaulCredentials = false 应该在设置凭据之前***.com/questions/2470645/…【参考方案7】:

快速回答:FROM 地址必须与您发送的帐户完全匹配,否则您将收到错误 5.7.1 客户端无权作为此发件人发送。

我的猜测是,这可以防止使用您的 Office 365 帐户进行电子邮件欺骗,否则您可以通过 sballmer@microsoft.com 发送电子邮件。

另一件事是在认证中,用域名填写第三个字段,比如

Dim smtpAuth = New System.Net.NetworkCredential(
    "TheDude", "hunter2password", "MicrosoftOffice365Domain.com")

如果这不起作用,请仔细检查您是否可以登录帐户:https://portal.microsoftonline.com

另外需要注意的是,作为反垃圾邮件解决方案,您的防病毒解决方案可能会阻止对端口 25 和 587 的编程访问。 Norton 和 McAfee 可能会静默阻止对这些端口的访问。只有启用 Mail 和 Socket 调试才能让您注意到它(见下文)。

最后要注意的是,Send 方法是异步。如果你打电话给

Dispose
在您调用 send 后立即处理,您很可能会在发送邮件之前关闭您的连接。让您的 smtpClient 实例监听 OnSendCompleted 事件,并从那里调用 dispose。您必须改用 SendAsync 方法,Send 方法不会引发此事件。

详细回答:使用 Visual Studio(VB.NET 或 C# 无关紧要),我制作了一个简单的表单,其中包含一个创建邮件消息的按钮,类似于上面的那个。然后我将它添加到 application.exe.config (在我项目的 bin/debug 目录中)。这使“输出”选项卡可以具有详细的调试信息。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.diagnostics>
        <sources>
            <source name="System.Net">
                <listeners>
                    <add name="System.Net" />
                </listeners>
            </source>
            <source name="System.Net.Sockets">
                <listeners>
                    <add name="System.Net" />
                </listeners>
            </source>
        </sources>
        <switches>
            <add name="System.Net" value="Verbose" />
            <add name="System.Net.Sockets" value="Verbose" />
        </switches>
        <sharedListeners>
            <add name="System.Net"
              type="System.Diagnostics.TextWriterTraceListener"
              initializeData="System.Net.log"
            />
        </sharedListeners>
        <trace autoflush="true" />
    </system.diagnostics>
</configuration>

【讨论】:

我假设hunter2password 不是您的密码? 嘿,是的,那是对 irc meme 的旧引用。我的密码是***************。来源:ars.userfriendly.org/cartoons/?id=19990814 如果您使用 O365 授权的“发送为”电子邮件地址添加 MailMessage.Sender 属性,则 MailMessage.From 不必是主帐户电子邮件。有关详细信息,请参阅我在此线程中的帖子:***.com/a/33613150/2464869 @jklemmack 谢谢,我想知道 bash.org 现在的名字是什么。 --- 什么叫名字没变!? :)【参考方案8】:

你见过吗? Sending email using Smtp.mail.microsoftonline.com

在设置 Credentials 之后设置 UseDefaultCredentials 将重置您的 Credentials 属性。

【讨论】:

以上是关于通过 Exchange Online (Office 365) 使用 System.Net.Mail 发送 SMTP 电子邮件的主要内容,如果未能解决你的问题,请参考以下文章

关于本地Exchange 2013 和 Exchange Online 组织之间的OAuth 身份验证的问题

通过 Exchange Online (Office 365) 使用 System.Net.Mail 发送 SMTP 电子邮件

在 Exchange Online 中管理非活动邮箱

易宝典文章——玩转Office 365中的Exchange Online服务 之二十八 怎样过滤病毒木马邮件

Install Office Online Server for Exchange 2016 CU3

Install Office Online Server for Exchange 2016 CU3