开始测试前,要确保发邮件的服务器的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
当一切都配置好了,开始执行,还是报上面的异常,后来发现还需要在配置文件加上 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;
}
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
不能对访问的目标提供有效证书。
到这里有两种解决方案:
1.在网上找个生成安全证书的方法按要求放在指定位置:http://www.oschina.net/question/12_19249
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还有很多不明白的地方,上面只是记录解决问题的过程,和大家共同学习。