如何根据新的安全策略在.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
切换到使用MailKit
和Google.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)
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和安全”>“关联的应用和网站”>“允许安全性较低的应用”>开