【问题标题】:Can't send mail via SSL or TLS using SMTP using Javamail无法使用 Javamail 使用 SMTP 通过 SSL 或 TLS 发送邮件
【发布时间】:2016-04-12 19:22:05
【问题描述】:

新年快乐!

我正在开发一个应用程序,只要发生特定触发器,用户就会收到一封电子邮件。

这是我用来发送电子邮件的功能:

public static void sendEmail(String host, String port, String useSSL, String useTLS, String useAuth, String user, String password, String subject, String content, String type, String recipients)
            throws NoSuchProviderException, AddressException, MessagingException  {
        final Properties props = new Properties();
        props.setProperty("mail.transport.protocol", "smtp");
        props.setProperty("mail.smtp.host", host);
        props.setProperty("mail.smtp.port", port);        
        if (useSSL != null && !useSSL.equals("false") && useSSL.equals("true")) {
            props.setProperty("mail.smtp.ssl.enable", useSSL);
            props.setProperty("mail.smtp.socketFactory.class",
                    "javax.net.ssl.SSLSocketFactory");
            props.setProperty("mail.smtp.socketFactory.port", port);

        }
        if (useTLS != null && !useTLS.equals("false") && useTLS.equals("true")) {
            props.setProperty("mail.smtp.starttls.enable", useTLS);
            props.setProperty("mail.smtp.socketFactory.fallback", "true");
        }   
        props.setProperty("mail.smtp.auth", useAuth);
        props.setProperty("mail.from", user);  
        props.setProperty("mail.smtp.user", user);
        props.setProperty("mail.password", password);

        Session mailSession = Session.getDefaultInstance(props, new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(props.getProperty("mail.smtp.user"), props
                        .getProperty("mail.password"));
            }
        });   

        Transport transport = mailSession.getTransport();

        MimeMessage message = new MimeMessage(mailSession);
        message.setHeader("Subject", subject);
        message.setContent(content, type);

        StringTokenizer tokenizer = new StringTokenizer(recipients, ";");
        while (tokenizer.hasMoreTokens()) {
            String recipient = tokenizer.nextToken();
            message.addRecipient(Message.RecipientType.TO,
                    new InternetAddress(recipient));
        }

        transport.connect();
        transport.sendMessage(message, message.getRecipients(Message.RecipientType.TO));
        transport.close();

奇怪的是,每当我尝试使用 main 方法运行上述代码时,它都会成功发送 SSL 和 TLS 协议的电子邮件。

public static void main(String args[])
    {
        try {
            Notifier.sendEmail("smtp.gmail.com", "587", "false", "true", "true","sender_email@gmail.com", "testpassword", "CHECKING SETTINGS", "CHECKING EMAIL FUNCTIONALITY", "text/html", "cc_email@gmail.com");
        } catch (Exception ex) {
            ex.printStackTrace();
        } 
    }

但每当我尝试通过我的 Web 应用程序运行相同的代码时,它都会失败。

通过 SSL 发送会引发此错误:

com.sun.mail.smtp.SMTPSendFailedException: 530-5.5.1 Authentication Required. Learn more at
jvm 1    | 530 5.5.1  https://support.google.com/mail/answer/14257 f12sm88286300pat.20 - gsmtp
jvm 1    | 
jvm 1    |  at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:2057)

通过 TLS 发送会引发此错误:

javax.mail.MessagingException: Could not connect to SMTP host: smtp.gmail.com, port: 587;
jvm 1    |   nested exception is:
jvm 1    |  javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
jvm 1    |  at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1934)

感谢任何形式的帮助。

编辑1:

这是来自前端的 tpl 文件

<div class="label1"><h3 class="label">Host:</h3></div>
     <div class="field1"><input type="text" class="input1" name="host" size="20" value="$HOST$"></div>
         <div class="port"><h3 class="label">Port:</h3></div>
         <div class="fieldport"><input type="text" class="fieldport" name="port" size="5" value="$PORT$"></div>
         <div class="ssl">
                <input type="radio" name="sslEnable" value="$SSLENABLE$">
                    Enable SSL?
         </div>
         <div class="tls">
                <input type="radio" name="tlsEnable" value="$TLSENABLE$">
                    Enable TLS?
         </div>
         <div class="auth">
                <input type="checkbox" name="auth"$AUTH$>
                    Enable Authentication?
         </div>            
     <div class="label2"><h3 class="label">User:</h3></div>
     <div class="field2"><input type="text" class="input1" name="user" size="20" value="$USER$"></div>
     <div class="label3"><h3 class="label">Password:</h3></div>
     <div class="field3"><input type="password" class="input1" name="password" size="20" value="$PASSWORD$"></div>
     <div class="label4"><h3 class="label">Recipient(s):</h3></div>
     <div class="field4"><input type="text" class="input1" name="recipients" size="50" value="$RECIPIENTS$"></div>

这些值保存在一个配置文件中,如下所示:

host=smtp.gmail.com
port=587
ssl=false
tls=true
auth=true
user=send_user_email@gmail.com
password=O0UbYboDfVFRaiA=
recipients=cc_user_email@gmail.com
trigger1=false
attempt=0
trigger2=false
percent=5
anyOrAll=ANY
trigger3=true
format=HTML
trigger4=true
trigger5=true

EDIT2:

public static void sendEmail(String message)
      throws NoSuchProviderException, AddressException, MessagingException
  {
    if (message == null || message.trim().equals("")) return;

    StringBuffer content = new StringBuffer();
    content.append(getHeader());
    content.append(message);
    content.append(getFooter());
    String format = NotifyProps.getFormat();
    String type = "text/plain";
    if (format.equals(NotifyProps.HTML)) type = "text/html";

    sendEmail(NotifyProps.getHost(), NotifyProps.getPort(), Boolean.toString(NotifyProps.getUseAuth()), Boolean.toString(NotifyProps.getUseSSL()), Boolean.toString(NotifyProps.getUseTLS()),NotifyProps.getUser(), NotifyProps.getPassword(),
              "Transaction Processor Auto Notification", content.toString(), type,
              NotifyProps.getRecipients())
  }

这是设置和获取属性的类:

https://codeshare.io/5G8ki

谢谢。

【问题讨论】:

  • 令我惊讶的是,您说它在命令行测试中有效。即使使用 TLS,您也应该将 mail.smtp.socketFactory.class 设置为 javax.net.ssl.SSLSocketFactory,而不仅仅是 SSL。但与此无关,我建议您使用 Yoda 条件来检查字符串常量,如"true".equals(sslStr)。它是 null 安全的,并且杂乱无章。

标签: java ssl smtp


【解决方案1】:

simple-java-mail 有一个简单的枚举,可用于指示 SSL 或 TLS。这样您就不必担心正确的属性:

Email email = new Email();

(...)

new Mailer("smtp.gmail.com", 25, "your user", "your password", TransportStrategy.SMTP_TLS).sendMail(email);
new Mailer("smtp.gmail.com", 587, "your user", "your password", TransportStrategy.SMTP_TLS).sendMail(email);
new Mailer("smtp.gmail.com", 465, "your user", "your password", TransportStrategy.SMTP_SSL).sendMail(email);

如果您启用了双因素登录,则需要从您的 Google 帐户生成 application specific password 才能使此示例正常运行。

【讨论】:

    【解决方案2】:

    最近,gmail 中有更新安全性。您必须在页面https://myaccount.google.com/security?pli=1 中允许“允许安全性较低的应用程序访问”选项。然后您就可以毫无问题地从您的帐户发送邮件了

    【讨论】:

      【解决方案3】:

      试试下面对我有用的代码。

      public void sendEmail(){
      
              Properties props = new Properties();
              props.put("mail.smtp.host", "smtp.gmail.com");
              props.put("mail.smtp.socketFactory.port", "465");
              props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
              props.put("mail.smtp.auth", "true");
              props.put("mail.smtp.port", "465");
      
      Session session = Session.getDefaultInstance(props,
                  new javax.mail.Authenticator() {
                      protected PasswordAuthentication getPasswordAuthentication() {
                          return new PasswordAuthentication("senderEmail@gmail.com","secret");
                      }
                  });
      
                  try {
                  Message message = new MimeMessage(session);
                  message.setFrom(new InternetAddress("from-email@gmail.com"));
                  message.setRecipients(Message.RecipientType.TO,
                          InternetAddress.parse("to-email@gmail.com"));
                  message.setSubject("This is testing message");
                  message.setText("Hi this is testing email....not spam");
      
              Transport.send(message);
                  System.out.println("email successfully sent..");
      
              } catch (MessagingException e) {
                  throw new RuntimeException(e);
              }
          }
      

      【讨论】:

        【解决方案4】:

        尽管您的代码看起来不错,但至少存在一个大问题。 您正在尝试为 TLS 和 SSL 使用相同的端口 (587)。 我不确定 TLS,但如果您将请求发送到端口 465,SSL 代码应该可以工作。如 @ 987654321@:

        在端口 465(使用 SSL)和端口 587(使用 TLS)上配置 SMTP 服务器[...]

        他们有自己的特定端口。您收到的 SSL 错误:

        Unrecognized SSL message, plaintext connection?
        

        您的客户端是否不理解它收到了非 SSL 编码的响应这一事实(由于 TLS 端口未实现 SSL)。

        【讨论】:

        • 我没有为两种协议使用相同的端口。我对 SSL 使用 465,对 TLS 使用 587。
        • 您能否向我们展示实际产生错误的 Web 代码?您包含了主要方法版本,但这已经有效。根据您正在尝试在 TLS 端口上启动 SSL 连接的错误消息。您的 webapp 中的参数列表可能有问题吗?
        • 如何读取该配置?前端代码与加载此配置的部分无关。这些配置条目(如端口)最终到达sendMail 函数。看看他们是否正确到达会很好。
        • 您用于最后一个链接的网站正在返回 HTTP 522,所以我无法检查。是否可以在您的 webapp 中记录参数以显示它获得的确切值?可能是读取的参数冲突,或者返回默认值,缓存结果...各种错误都可能导致最终传递错误的端口。
        【解决方案5】:

        我们为 TLS 设置了更多属性

        props.put("mail.smtp.starttls.enable", "true");
        props.setProperty("mail.smtp.ssl.enable", "true");
        props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        props.put("mail.smtp.socketFactory.fallback", "false");
        

        对于 Auth 使用 smtps 而不是 smtp

        props.setProperty("mail.smtps.auth", useAuth);
        

        在获得交通工具时

        session.getTransport("smtps"); 
        

        在连接时再次传递主机电子邮件和密码

        transport.connect("smtp.gmail.com", "user@email", "password");
        

        用于调试

        session.setDebug(true);
        

        【讨论】:

          猜你喜欢
          • 2012-09-10
          • 2015-08-25
          • 2014-09-13
          • 1970-01-01
          • 2010-12-26
          • 2020-06-24
          • 2013-09-03
          • 1970-01-01
          • 2013-11-28
          相关资源
          最近更新 更多