This commit is contained in:
Looly 2024-08-08 14:04:16 +08:00
parent 6e10a83647
commit 6e8c743ec5
3 changed files with 147 additions and 35 deletions

View File

@ -14,15 +14,28 @@ package org.dromara.hutool.extra.mail;
import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IORuntimeException;
import java.nio.charset.Charset;
/** /**
* 全局邮件帐户依赖于邮件配置文件{@link MailAccount#MAIL_SETTING_PATHS} * 全局邮件帐户依赖于邮件配置文件{@link MailAccount#MAIL_SETTING_PATHS}
* *
* @author looly * @author looly
*
*/ */
public enum GlobalMailAccount { public enum GlobalMailAccount {
/**
* 单例
*/
INSTANCE; INSTANCE;
// mime
private static final String SPLIT_LONG_PARAMS = "mail.mime.splitlongparameters";
private static final String CHARSET = "mail.mime.charset";
static {
System.setProperty(SPLIT_LONG_PARAMS, "false");
System.setProperty(CHARSET, INSTANCE.mailAccount.getCharset().name());
}
private final MailAccount mailAccount; private final MailAccount mailAccount;
/** /**
@ -41,6 +54,28 @@ public enum GlobalMailAccount {
return this.mailAccount; return this.mailAccount;
} }
/**
* 设置对于超长参数是否切分为多份默认为false国内邮箱附件不支持切分的附件名<br>
* 注意此项为全局设置此项会调用
* <pre>
* System.setProperty("mail.mime.splitlongparameters", true)
* </pre>
*
* @param splitLongParams 对于超长参数是否切分为多份
*/
public void setSplitLongParams(final boolean splitLongParams) {
System.setProperty(SPLIT_LONG_PARAMS, String.valueOf(splitLongParams));
}
/**
* 设置全局默认编码
*
* @param charset 编码
*/
public void setCharset(final Charset charset) {
System.setProperty(CHARSET, charset.name());
}
/** /**
* 创建默认帐户 * 创建默认帐户
* *

View File

@ -13,6 +13,7 @@
package org.dromara.hutool.extra.mail; package org.dromara.hutool.extra.mail;
import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
@ -36,6 +37,8 @@ public class MailAccount implements Serializable {
private static final String SMTP_HOST = "mail.smtp.host"; private static final String SMTP_HOST = "mail.smtp.host";
private static final String SMTP_PORT = "mail.smtp.port"; private static final String SMTP_PORT = "mail.smtp.port";
private static final String SMTP_AUTH = "mail.smtp.auth"; private static final String SMTP_AUTH = "mail.smtp.auth";
// 认证机制多个机制使用空格或逗号隔开XOAUTH2
private static final String SMTP_AUTH_MECHANISMS = "mail.smtp.auth.mechanisms";
private static final String SMTP_TIMEOUT = "mail.smtp.timeout"; private static final String SMTP_TIMEOUT = "mail.smtp.timeout";
private static final String SMTP_CONNECTION_TIMEOUT = "mail.smtp.connectiontimeout"; private static final String SMTP_CONNECTION_TIMEOUT = "mail.smtp.connectiontimeout";
private static final String SMTP_WRITE_TIMEOUT = "mail.smtp.writetimeout"; private static final String SMTP_WRITE_TIMEOUT = "mail.smtp.writetimeout";
@ -48,9 +51,6 @@ public class MailAccount implements Serializable {
private static final String SOCKET_FACTORY_FALLBACK = "mail.smtp.socketFactory.fallback"; private static final String SOCKET_FACTORY_FALLBACK = "mail.smtp.socketFactory.fallback";
private static final String SOCKET_FACTORY_PORT = "smtp.socketFactory.port"; private static final String SOCKET_FACTORY_PORT = "smtp.socketFactory.port";
// System Properties
private static final String SPLIT_LONG_PARAMS = "mail.mime.splitlongparameters";
// 其他 // 其他
private static final String MAIL_DEBUG = "mail.debug"; private static final String MAIL_DEBUG = "mail.debug";
@ -71,6 +71,10 @@ public class MailAccount implements Serializable {
* 是否需要用户名密码验证 * 是否需要用户名密码验证
*/ */
private Boolean auth; private Boolean auth;
/**
* 认证机制多个机制使用空格或逗号隔开XOAUTH2
*/
private String authMechanisms;
/** /**
* 用户名 * 用户名
*/ */
@ -94,10 +98,6 @@ public class MailAccount implements Serializable {
* 编码用于编码邮件正文和发送人收件人等中文 * 编码用于编码邮件正文和发送人收件人等中文
*/ */
private Charset charset = CharsetUtil.UTF_8; private Charset charset = CharsetUtil.UTF_8;
/**
* 对于超长参数是否切分为多份默认为false国内邮箱附件不支持切分的附件名
*/
private boolean splitlongparameters = false;
/** /**
* 对于文件名是否使用{@link #charset}编码默认为 {@code true} * 对于文件名是否使用{@link #charset}编码默认为 {@code true}
*/ */
@ -174,6 +174,14 @@ public class MailAccount implements Serializable {
*/ */
public MailAccount(final Setting setting) { public MailAccount(final Setting setting) {
setting.toBean(this); setting.toBean(this);
// custom property
// 对于用户希望直接在配置文件中设置mail.xxx参数的情况在此加入
setting.forEach((key, value) -> {
if (StrUtil.startWith(key, "mail.")) {
this.setCustomProperty(key, value);
}
});
} }
// -------------------------------------------------------------- Constructor end // -------------------------------------------------------------- Constructor end
@ -238,6 +246,26 @@ public class MailAccount implements Serializable {
return this; return this;
} }
/**
* 获取认证机制多个机制使用空格或逗号隔开XOAUTH2
*
* @return 认证机制
*/
public String getAuthMechanisms() {
return this.authMechanisms;
}
/**
* 设置认证机制多个机制使用空格或逗号隔开XOAUTH2
*
* @param authMechanisms 认证机制
* @return this
*/
public MailAccount setAuthMechanisms(final String authMechanisms) {
this.authMechanisms = authMechanisms;
return this;
}
/** /**
* 获取用户名 * 获取用户名
* *
@ -349,28 +377,6 @@ public class MailAccount implements Serializable {
return this; return this;
} }
/**
* 对于超长参数是否切分为多份默认为false国内邮箱附件不支持切分的附件名
*
* @return 对于超长参数是否切分为多份
*/
public boolean isSplitlongparameters() {
return splitlongparameters;
}
/**
* 设置对于超长参数是否切分为多份默认为false国内邮箱附件不支持切分的附件名<br>
* 注意此项为全局设置此项会调用
* <pre>
* System.setProperty("mail.mime.splitlongparameters", true)
* </pre>
*
* @param splitlongparameters 对于超长参数是否切分为多份
*/
public void setSplitlongparameters(final boolean splitlongparameters) {
this.splitlongparameters = splitlongparameters;
}
/** /**
* 对于文件名是否使用{@link #charset}编码默认为 {@code true} * 对于文件名是否使用{@link #charset}编码默认为 {@code true}
* *
@ -583,14 +589,15 @@ public class MailAccount implements Serializable {
* @return {@link Properties} * @return {@link Properties}
*/ */
public Properties getSmtpProps() { public Properties getSmtpProps() {
//全局系统参数
System.setProperty(SPLIT_LONG_PARAMS, String.valueOf(this.splitlongparameters));
final Properties p = new Properties(); final Properties p = new Properties();
p.put(MAIL_PROTOCOL, "smtp"); p.put(MAIL_PROTOCOL, "smtp");
p.put(SMTP_HOST, this.host); p.put(SMTP_HOST, this.host);
p.put(SMTP_PORT, String.valueOf(this.port)); p.put(SMTP_PORT, String.valueOf(this.port));
p.put(SMTP_AUTH, String.valueOf(this.auth)); p.put(SMTP_AUTH, String.valueOf(this.auth));
// issue#3687 增加Oath2认证方式支持
if(StrUtil.isNotBlank(this.authMechanisms)){
p.put(SMTP_AUTH_MECHANISMS, this.authMechanisms);
}
if (this.timeout > 0) { if (this.timeout > 0) {
p.put(SMTP_TIMEOUT, String.valueOf(this.timeout)); p.put(SMTP_TIMEOUT, String.valueOf(this.timeout));
} }
@ -638,7 +645,7 @@ public class MailAccount implements Serializable {
* @return this * @return this
*/ */
public MailAccount defaultIfEmpty() { public MailAccount defaultIfEmpty() {
// 去掉发件人的姓名部分 Assert.notBlank(this.from, "'from' must not blank!");
final String fromAddress = InternalMailUtil.parseFirstAddress(this.from, this.charset).getAddress(); final String fromAddress = InternalMailUtil.parseFirstAddress(this.from, this.charset).getAddress();
if (StrUtil.isBlank(this.host)) { if (StrUtil.isBlank(this.host)) {
@ -669,6 +676,6 @@ public class MailAccount implements Serializable {
@Override @Override
public String toString() { public String toString() {
return "MailAccount [host=" + host + ", port=" + port + ", auth=" + auth + ", user=" + user + ", pass=" + (ArrayUtil.isEmpty(this.pass) ? "" : "******") + ", from=" + from + ", startttlsEnable=" return "MailAccount [host=" + host + ", port=" + port + ", auth=" + auth + ", user=" + user + ", pass=" + (ArrayUtil.isEmpty(this.pass) ? "" : "******") + ", from=" + from + ", startttlsEnable="
+ starttlsEnable + ", socketFactoryClass=" + socketFactoryClass + ", socketFactoryFallback=" + socketFactoryFallback + ", socketFactoryPort=" + socketFactoryPort + "]"; + starttlsEnable + ", socketFactoryClass=" + socketFactoryClass + ", socketFactoryFallback=" + socketFactoryFallback + ", socketFactoryPort=" + socketFactoryPort + "]";
} }
} }

View File

@ -0,0 +1,70 @@
package org.dromara.hutool.extra.mail;
import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import org.dromara.hutool.core.lang.Console;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.Properties;
public class Oauth2Test {
@Test
@Disabled
void sendTest() {
final MailAccount mailAccount = new MailAccount();
mailAccount.setHost("smtp.office365.com");
mailAccount.setPort(587);
mailAccount.setFrom("xxxx@outlook.com");
mailAccount.setUser("xxxx");
// 这里使用生成的token
mailAccount.setPass("token".toCharArray());
mailAccount.setAuth(true);
mailAccount.setStarttlsEnable(true);
// 这里关掉SSL
mailAccount.setSslEnable(false);
// 使用XOAUTH2
mailAccount.setCustomProperty("mail.smtp.auth.mechanisms", "XOAUTH2");
mailAccount.setCustomProperty("mail.smtp.auth.login.disable", "true");
mailAccount.setCustomProperty("mail.smtp.auth.plain.disable", "true");
final String id = Mail.of(mailAccount)
.setTos("xxx@qq.com")
.setContent("Mail test from Outlook!")
.setTitle("测试Outlook邮件")
.send();
Console.log(id);
}
/**
* https://medium.com/@tempmailwithpassword/sending-emails-with-java-using-oauth2-and-office-365-b164d54f68fc
*
* @throws MessagingException 异常
*/
@Test
@Disabled
void sendTest2() throws MessagingException {
final Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.office365.com");
props.put("mail.smtp.port", "587");
props.put("mail.smtp.auth.mechanisms", "XOAUTH2");
props.put("mail.smtp.auth.login.disable", "true");
props.put("mail.smtp.auth.plain.disable", "true");
final Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("xx", "123");
}
});
final MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("xxx@outlook.com"));
message.addRecipient(Message.RecipientType.TO, new InternetAddress("xxx@qq.com"));
message.setSubject("Your Subject Here");
message.setText("Email body content here.");
Transport.send(message);
}
}