【问题标题】:Confused with the SmtpClient.UseDefaultCredentials Property与 SmtpClient.UseDefaultCredentials 属性混淆
【发布时间】:2012-08-28 20:54:43
【问题描述】:

在我的 MVC4 应用程序中,我使用 SmtpClient 通过 Gmail 的 smtp.gmail.com SMTP 服务器发送电子邮件。

我已经使用以下设置配置了我的 Web.Config 文件:

<system.net>
 <mailSettings>
  <smtp deliveryMethod="Network">
   <network enableSsl="true" 
   defaultCredentials="false" 
   host="smtp.gmail.com" 
   port="587" 
   userName="xxMyUserNamexx@gmail.com" 
   password="xxMyPasswordxx" />
  </smtp>
 </mailSettings>
</system.net>

使用 SmtpClient 并发送电子邮件的方法如下所示:

public void SendMail(string fromDisplayName, string fromEmailAddress, string toEmailAddress, string subject, string body)
{
    MailAddress from = new MailAddress(fromEmailAddress, fromDisplayName);
    MailAddress to = new MailAddress(toEmailAddress);
    MailMessage mailMessage = new MailMessage(from, to);
    mailMessage.Body = body;
    mailMessage.Subject = subject;

    SmtpClient client = new SmtpClient();
    //client.UseDefaultCredentials = false;
    client.Send(mailMessage);
}

上面的代码按预期工作并且很好。让我感到困惑的是注释行 client.UseDefaultCredentials = false; - 如果我取消注释该行,我将收到一条异常消息,指出:

SMTP 服务器需要安全连接,或者客户端没有 认证。服务器响应为:5.5.1 Authentication Required。

更重要的是,无论我将UseDefaultCredentials属性设置为true还是false,我仍然会收到异常消息。我避免异常消息的唯一方法是完全删除该行。

这种行为正常吗?您能解释一下为什么我会收到异常消息吗?

【问题讨论】:

    标签: asp.net-mvc smtpclient


    【解决方案1】:

    那我为什么要显式设置属性为 false 抛出异常呢?

    这是因为UseDefaultCredentials 的设置器将Credentials 属性设置为null,如果您将其设置为false,或者如果设置为true,它会将其设置为CredentialCache.DefaultNetworkCredentials 属性。 DefaultNetworkCredentials 属性为 defined by MSDN as

    DefaultNetworkCredentials 返回的凭据表示应用程序正在运行的当前安全上下文的身份验证凭据。对于客户端应用程序,这些通常是运行应用程序的用户的 Windows 凭据(用户名、密码和域)。对于 ASP.NET 应用程序,默认网络凭据是登录用户或被模拟用户的用户凭据。

    当您将UseDefaultCredentials 设置为 true 时,它​​使用的是您的 IIS 用户,我假设您的 IIS 用户没有与您使用的任何 SMTP 服务器的帐户相同的身份验证凭据。将 UseDefaultCredentials 设置为 false null 会排除已设置的凭据。因此,无论哪种方式,您都会遇到该错误。

    下面是使用dotPeekUseDefaultCredentials 的设置器:

    set
    {
        if (this.InCall)
        {
            throw new InvalidOperationException(
                SR.GetString("SmtpInvalidOperationDuringSend"));
        }
        this.transport.Credentials = value 
            ? (ICredentialsByHost) CredentialCache.DefaultNetworkCredentials 
            : (ICredentialsByHost) null;
      }
    

    【讨论】:

      【解决方案2】:

      我收到了同样的信息,这让我发疯了。阅读此线程后,我意识到顺序对设置我的凭据很重要。这有效:

      client.UseDefaultCredentials = false;
      client.Credentials = new NetworkCredential(smtpSettings.Username, smtpSettings.Password);
      

      虽然这产生了您描述的错误:

      client.Credentials = new NetworkCredential(smtpSettings.Username, smtpSettings.Password);
      client.UseDefaultCredentials = false;
      

      对于遇到同样问题的其他人,这只是一个仅供参考。

      【讨论】:

      • Dave Zych 接受的回答解释了为什么会发生这种情况。
      【解决方案3】:

      此选项将设置客户端使用当前登录的用户

      的默认凭据

      如果您将其设置为 true,那么它将尝试使用用户的凭据。如果您将其设置为 false,那么它将使用为客户端的 Credentials 属性设置的值显式,如果它们未显式设置,那么它将尝试匿名连接,如您所见.

      【讨论】:

      • 我最初也是这么想的。但是,客户端没有匿名连接 - 它使用 Web.Config 文件中设置的凭据进行连接(如 OP 中所述)。更不用说,MS 规范说UseDefaultCredentials is false by default。那么我为什么要显式设置属性为 false 抛出异常呢?
      【解决方案4】:

      这就是我们可以使用或不使用 NetworkCredentials 创建 SMTP 客户端的方法。我正在使用此代码发送电子邮件。我们应该使用 client.UseDefaultCredentials 仅当我们不传递凭据并默认进行时。

              private SmtpClient InitializeSMTPClient()
              {
                  var client = new SmtpClient(_smtpServer, _smtpPort);
      
                  client.UseDefaultCredentials = _useSMTPDefaultCredentials;
      
                  if (_useSMTPDefaultCredentials)
                      return client;
      
                  var credentials = new NetworkCredential(_smtpUsername, _smtpPassword);
                  client.Credentials = credentials;
      
                  return client;
              }
      
      
      
              SMTPEmailResult SendSMTPEmail(List<string> to_email, List<string> ccEmails, string subject, string message)
              {
                  try
                  {
                      using (var client = InitializeSMTPClient())
                      {
                          var mail_message = GetMailMessage(to_email, ccEmails, subject, message);
                          log.Debug("Sending SMTP email.");
                          client.Send(mail_message);
                          log.Debug("SMTP email sent successfully.");
                          return SMTPEmailResult.SendSuccess;
                      }
                  }
                  catch (Exception ex)
                  {
                      log.Error(ex.Message, ex);
                      return SMTPEmailResult.SendFailed;
                  }
              }
      

      【讨论】: