java Mail發郵件 smtp被TLS加密認證不了的解決方案
阿新 • • 發佈:2019-01-28
開始測試前,要確保發郵件的伺服器的smtp服務可用。
不然會丟擲異常:
Sending the email to the following server failed : m.xxx.com:25
Caused by: javax.mail.AuthenticationFailedException: 334 NTLM supported
然後介紹下我的開發環境(context):目前用的開發框架是playframework,它幫忙封裝了apache的mail工具類,程式碼如下:
public static void sendMail(SendMailDto sendMailDto){ if(sendMailDto!=null){ HtmlEmail email = new HtmlEmail(); email.setCharset("UTF-8");// 編碼格式 try { email.addTo(sendMailDto.accepterEmail);// 接收者 email.setFrom(sendMailDto.sender, sendMailDto.name);// 傳送者,姓名 email.setSubject(sendMailDto.title);// 郵件標題 email.setMsg(sendMailDto.content);// 傳送內容 Mail.send(email); Logger.info("接收郵件: "+sendMailDto.accepterEmail+" 傳送成功!"); Logger.info("傳送郵件伺服器: "+sendMailDto.sender); Logger.info("傳送郵件名: "+sendMailDto.name); }catch (Exception e) { Logger.info("郵件: "+sendMailDto.accepterEmail+" 傳送失敗!"); e.printStackTrace(); } } }
在配置檔案中要申明:
mail.smtp.host=xxxx
mail.smtp.user=xxxx
mail.smtp.pass=xxxx
本質上還是用的apache的setMailSession()方法。
當一切都配置好了,開始執行,還是報上面的異常,後來發現還需要在配置檔案加上 mail.smtp.protocol=smtps,分析下原始碼:
public static Session getSession() { if (session == null) { Properties props = new Properties(); // Put a bogus value even if we are on dev mode, otherwise JavaMail will complain props.put("mail.smtp.host", Play.configuration.getProperty("mail.smtp.host", "localhost")); String channelEncryption; if (Play.configuration.containsKey("mail.smtp.protocol") && Play.configuration.getProperty("mail.smtp.protocol", "smtp").equals("smtps")) { // Backward compatibility before stable5 channelEncryption = "starttls"; } else { channelEncryption = Play.configuration.getProperty("mail.smtp.channel", "clear"); } if (channelEncryption.equals("clear")) { props.put("mail.smtp.port", "25"); } else if (channelEncryption.equals("ssl")) { // port 465 + setup yes ssl socket factory (won't verify that the server certificate is signed with a root ca.) props.put("mail.smtp.port", "465"); props.put("mail.smtp.socketFactory.port", "465"); props.put("mail.smtp.socketFactory.class", "play.utils.YesSSLSocketFactory"); props.put("mail.smtp.socketFactory.fallback", "false"); } else if (channelEncryption.equals("starttls")) { // port 25 + enable starttls + ssl socket factory props.put("mail.smtp.port", "25"); props.put("mail.smtp.starttls.enable", "true"); // can't install our socket factory. will work only with server that has a signed certificate // story to be continued in javamail 1.4.2 : https://glassfish.dev.java.net/issues/show_bug.cgi?id=5189 } if (Play.configuration.containsKey("mail.smtp.localhost")) { props.put("mail.smtp.localhost", Play.configuration.get("mail.smtp.localhost")); //override defaults } if (Play.configuration.containsKey("mail.smtp.socketFactory.class")) { props.put("mail.smtp.socketFactory.class", Play.configuration.get("mail.smtp.socketFactory.class")); } if (Play.configuration.containsKey("mail.smtp.port")) { props.put("mail.smtp.port", Play.configuration.get("mail.smtp.port")); } String user = Play.configuration.getProperty("mail.smtp.user"); String password = Play.configuration.getProperty("mail.smtp.pass"); if (password == null) { // Fallback to old convention password = Play.configuration.getProperty("mail.smtp.password"); } String authenticator = Play.configuration.getProperty("mail.smtp.authenticator"); session = null; if (authenticator != null) { props.put("mail.smtp.auth", "true"); try { session = Session.getInstance(props, (Authenticator) Play.classloader.loadClass(authenticator).newInstance()); } catch (Exception e) { Logger.error(e, "Cannot instanciate custom SMTP authenticator (%s)", authenticator); } } if (session == null) { if (user != null && password != null) { props.put("mail.smtp.auth", "true"); session = Session.getInstance(props, new SMTPAuthenticator(user, password)); } else { props.remove("mail.smtp.auth"); session = Session.getInstance(props); } } if (Boolean.parseBoolean(Play.configuration.getProperty("mail.debug", "false"))) { session.setDebug(true); } } return session; }
為了向後相容,需要在session中設定:
props.put("mail.smtp.port", "25");
props.put("mail.smtp.starttls.enable", "true");
配置好了以後再來嘗試下,發現還是有異常丟擲:
javax.mail.MessagingException: Could not convert socket to TLS sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
不能把socket解析為TLS(通過上面的截圖,可以看到我的郵件伺服器是TLS加密了的)
不能對訪問的目標提供有效證書。
到這裡有兩種解決方案:
2.把當前smtp host設為可信任 props.put("mail.smtp.ssl.trust", "smtp伺服器地址")
到此 所有問題都解決了。
總結下:
產生第一個異常的原因有以下3個:
1. 郵件伺服器的smtp沒有開啟。
2. 使用者名稱密碼錯誤。
3. mail工具類版本較低,不能有效生成socket factory
解決方案:登入郵箱,在設定裡面開啟smtp服務,驗證程式中登入郵箱的使用者名稱密碼填寫正確,用高版本的mail.jar 或者在session中設定props.put("mail.smtp.port", "25"); props.put("mail.smtp.starttls.enable", "true");
產生第二異常的原因:
smtp設定了加密,或者嘗試訪問不受信任地址
解決方案:用工具類生成安全證書,放在\jdk1.6.0_31\jre\lib\security下面
關於apache的mail還有很多不明白的地方,上面只是記錄解決問題的過程,和大家共同學習。