如何根据新的安全策略在.Net中发送电子邮件?

为了更好地保护用户,GMail和其他邮件提供商build议将我们的所有应用程序升级到OAuth 2.0。

我是对的,这意味着System.Net.Mail不再工作,我们需要使用另一个库,如MailKit

一般来说,我试图了解如何发送电子邮件,而不允许“访问不太安全的应用程序”?

因为我有System.Net.Mail.SmtpException: The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.5.1 Authentication Required. System.Net.Mail.SmtpException: The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.5.1 Authentication Required.smtpClient.Send(message); 执行。

如果解决这个问题的唯一方法是使用MailKit ,我认为这个问题将是一个很好的切实可行的从System.Net.Mail切换到使用MailKitGoogle.Apis.Auth.OAuth2 。 我不知道也许一般的解决scheme将使用DotNetOpenAuth

我在我的应用程序中有以下类,它们对应于发送电子邮件到任何地址(gmail,yandex和其他):

 public class EmailSender { public void SendEmail(SmtpServerSettings serverSettings, SendEmailRequest emailRequest) { // Usually I have 587 port, SmtpServerName = smtp.gmail.com _logger.Trace("Sending message with subject '{0}' using SMTP server {1}:{2}", emailRequest.Subject, serverSettings.SmtpServerName, serverSettings.SmtpPort); try { using (var smtpClient = new SmtpClient(serverSettings.SmtpServerName, (int)serverSettings.SmtpPort)) { smtpClient.EnableSsl = serverSettings.SmtpUseSsl; // true if (!string.IsNullOrEmpty(serverSettings.UserName) || !string.IsNullOrEmpty(serverSettings.EncryptedPassword)) { smtpClient.Credentials = new NetworkCredential(serverSettings.UserName, serverSettings.EncryptedPassword); } smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network; smtpClient.Timeout = (int)serverSettings.SmtpTimeout.TotalMilliseconds; using (var message = new MailMessage()) { message.From = new MailAddress(serverSettings.FromAddress); emailRequest.To.ForEach(message.To.Add); emailRequest.CC.ForEach(message.CC.Add); emailRequest.Bcc.ForEach(message.Bcc.Add); message.Subject = emailRequest.Subject.Replace('\r', ' ').Replace('\n', ' '); message.Body = emailRequest.Body; message.BodyEncoding = Encoding.UTF8; message.IsBodyHtml = false; smtpClient.Send(message); } } _logger.Trace("Sent message with subject '{0}' using SMTP server {1}:{2}", emailRequest.Subject, serverSettings.SmtpServerName, serverSettings.SmtpPort); } catch (SmtpFailedRecipientsException e) { var failedRecipients = e.InnerExceptions.Select(x => x.FailedRecipient); LogAndReThrowWithValidMessage(e, EmailsLocalization.EmailDeliveryFailed, failedRecipients); } } } 

它工作正常,直到新的Google安全策略 。

我知道System.Net.Mail不支持OAuth2 。 我决定使用MailKit's SmtpClient发送消息。

调查后,我明白,我的初始代码没有太大的变化,因为MailKit's API看起来非常相似(与System.Net.Mail )。

除了一个细节外:我需要拥有用户的OAuth访问令牌(MailKit没有可以获取OAuth令牌的代码,但是如果有的话可以使用它)。

所以在将来我会有以下的路线:

 smtpClient.Authenticate (usersLoginName, usersOAuthToken); 

我有一个想法将GoogleCredentials作为新的参数添加到SendEmail方法:

 public void SendEmail(SmtpServerSettings serverSettings, SendEmailRequest emailRequest, GoogleCredentials credentials) { var certificate = new X509Certificate2(credentials.CertificateFilePath, credentials.PrivateKey, X509KeyStorageFlags.Exportable); var credential = new ServiceAccountCredential( new ServiceAccountCredential.Initializer(credentials.ServiceAccountEmail) { Scopes = new[] { "https://mail.google.com/" }, User = serverSettings.UserName }.FromCertificate(certificate)); .... //my previous code but with MailKit API } 

如何获取用户usersOAuthToken ? 使用Google.Apis.Auth.OAuth2是否是最佳实践技巧?

我上面发布的代码仅适用于GMail,不适用于yandex.ru或其他邮件提供商。 要与他人合作,我可能需要使用另一个OAuth2库。 但是我不想在许多可能的邮件提供者的代码中拥有许多authentication机制。 我希望为每个邮件提供商提供一个通用解决scheme。 还有一个可以发送电子邮件的库(如.net smtpclient)

一般的解决scheme是https://galleryserverpro.com/use-gmail-as-your-smtp-server-even-when-using-2-factor-authentication-2-step-verification/

1)使用浏览器login到您的Google帐户,然后转到login和安全设置。 查找两步validation设置。

2)如果两步validation是closures的,并且您希望保持这种方式,那就意味着您将需要实现许多auth机制。

解决scheme:将其打开,然后生成并使用Google应用密码。 它应该工作! 您不需要使用其他库,如mailkit

如何获取用户OAuthToken?

您需要做的第一件事是按照Google的说明为您的应用程序获取OAuth 2.0凭据。

完成之后,获取访问令牌的最简单方法是使用Google的Google.Apis.Auth库:

 using System; using System.Threading; using System.Security.Cryptography.X509Certificates; using Google.Apis.Auth.OAuth2; using MimeKit; using MailKit.Net.Smtp; using MailKit.Security; namespace Example { class Program { public static async void Main (string[] args) { var certificate = new X509Certificate2 (@"C:\path\to\certificate.p12", "password", X509KeyStorageFlags.Exportable); var credential = new ServiceAccountCredential (new ServiceAccountCredential .Initializer ("your-developer-id@developer.gserviceaccount.com") { // Note: other scopes can be found here: https://developers.google.com/gmail/api/auth/scopes Scopes = new[] { "https://mail.google.com/" }, User = "username@gmail.com" }.FromCertificate (certificate)); // Note: result will be true if the access token was received successfully bool result = await credential.RequestAccessTokenAsync (CancellationToken.None); if (!result) { Console.WriteLine ("Error fetching access token!"); return; } var message = new MimeMessage (); message.From.Add (new MailboxAddress ("Your Name", "username@gmail.com")); message.To.Add (new MailboxAddress ("Recipient's Name", "recipient@yahoo.com")); message.Subject = "This is a test message"; var builder = new BodyBuilder (); builder.TextBody = "This is the body of the message."; builder.Attachments.Add (@"C:\path\to\attachment"); message.Body = builder.ToMessageBody (); using (var client = new SmtpClient ()) { client.Connect ("smtp.gmail.com", 587, SecureSocketOptions.StartTls); // use the access token as the password string client.Authenticate ("username@gmail.com", credential.Token.AccessToken); client.Send (message); client.Disconnect (true); } } } } 

使用Google.Apis.Auth.OAuth2是否是最佳实践技巧?

为什么不使用他们的API来获取身份validation令牌? 这似乎是最好的方式来得到我…

我可以发送电子邮件到其他非Gmail账户吗?

是的,您通过GMail发送的任何电子邮件都可以发送到任何其他电子邮件地址 – 它不一定只针对其他GMail地址。

当未使用Google特定安全要求的应用程序使用Gmail smtp服务器时,会出现身份validation错误。 在Gmail帐户设置中,启用:“login和安全”>“关联的应用和网站”>“允许安全性较低的应用”>开