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

时间:2016-01-18 09:40:42

标签: c# .net

为了更好地保护您的用户,GMail和其他邮件提供商建议将我们的所有应用程序升级到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.执行了smtpClient.Send(message);

如果解决此问题的唯一方法是使用MailKit,我认为这个问题将是一个很好的实用逐步切换教程,从System.Net.Mail到使用MailKitGoogle.Apis.Auth.OAuth2。我不知道通用解决方案可能会使用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);
        }
   }
}

new Google security policies之前一切正常。

我知道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库。但我不想在我的代码中为许多可能的邮件提供商提供许多身份验证机制。我想为每个邮件提供商提供一个通用解决方案。还有一个可以发送电子邮件的库(比如.net smtpclient)

3 个答案:

答案 0 :(得分:29)

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

1)使用浏览器登录您的Google帐户,然后转到登录&安全设定。查找两步验证设置。

2)如果两步验证已关闭,并且您希望保持这种方式,则意味着您需要实现许多身份验证机制,如您所说。

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

答案 1 :(得分:17)

  

如何获得usersOAuthToken?

您需要做的第一件事就是按照Google's instructions获取应用程序的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获取身份验证令牌?这似乎是最好的方式让我...

  

我是否可以向其他非Gmail帐户发送电子邮件?

是的,您通过GMail发送的任何电子邮件都可以发送到任何其他电子邮件地址 - 它不必只是其他GMail地址。

答案 2 :(得分:-1)

从未实现Google特定安全要求的应用程序使用Gmail smtp服务器时发生身份验证错误。在Gmail帐户设置中,启用:       "登录&安全" >           "已连接的应用&网站及#34; >               “允许安全性较低的应用”>                   上