diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/mail/GlobalMailAccount.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/mail/GlobalMailAccount.java
index f1e392a11..2d14c05ea 100644
--- a/hutool-extra/src/main/java/org/dromara/hutool/extra/mail/GlobalMailAccount.java
+++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/mail/GlobalMailAccount.java
@@ -14,15 +14,28 @@ package org.dromara.hutool.extra.mail;
import org.dromara.hutool.core.io.IORuntimeException;
+import java.nio.charset.Charset;
+
/**
* 全局邮件帐户,依赖于邮件配置文件{@link MailAccount#MAIL_SETTING_PATHS}
*
* @author looly
- *
*/
public enum GlobalMailAccount {
+ /**
+ * 单例
+ */
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;
/**
@@ -41,6 +54,28 @@ public enum GlobalMailAccount {
return this.mailAccount;
}
+ /**
+ * 设置对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名)
+ * 注意此项为全局设置,此项会调用
+ *
+ * System.setProperty("mail.mime.splitlongparameters", true)
+ *
+ *
+ * @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());
+ }
+
/**
* 创建默认帐户
*
diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/mail/MailAccount.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/mail/MailAccount.java
index 2e2d242a9..94be9cc12 100644
--- a/hutool-extra/src/main/java/org/dromara/hutool/extra/mail/MailAccount.java
+++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/mail/MailAccount.java
@@ -13,6 +13,7 @@
package org.dromara.hutool.extra.mail;
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.ObjUtil;
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_PORT = "mail.smtp.port";
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_CONNECTION_TIMEOUT = "mail.smtp.connectiontimeout";
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_PORT = "smtp.socketFactory.port";
- // System Properties
- private static final String SPLIT_LONG_PARAMS = "mail.mime.splitlongparameters";
-
// 其他
private static final String MAIL_DEBUG = "mail.debug";
@@ -71,6 +71,10 @@ public class MailAccount implements Serializable {
* 是否需要用户名密码验证
*/
private Boolean auth;
+ /**
+ * 认证机制,多个机制使用空格或逗号隔开,如:XOAUTH2
+ */
+ private String authMechanisms;
/**
* 用户名
*/
@@ -94,10 +98,6 @@ public class MailAccount implements Serializable {
* 编码用于编码邮件正文和发送人、收件人等中文
*/
private Charset charset = CharsetUtil.UTF_8;
- /**
- * 对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名)
- */
- private boolean splitlongparameters = false;
/**
* 对于文件名是否使用{@link #charset}编码,默认为 {@code true}
*/
@@ -174,6 +174,14 @@ public class MailAccount implements Serializable {
*/
public MailAccount(final Setting setting) {
setting.toBean(this);
+
+ // custom property
+ // 对于用户希望直接在配置文件中设置mail.xxx参数的情况,在此加入
+ setting.forEach((key, value) -> {
+ if (StrUtil.startWith(key, "mail.")) {
+ this.setCustomProperty(key, value);
+ }
+ });
}
// -------------------------------------------------------------- Constructor end
@@ -238,6 +246,26 @@ public class MailAccount implements Serializable {
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;
}
- /**
- * 对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名)
- *
- * @return 对于超长参数是否切分为多份
- */
- public boolean isSplitlongparameters() {
- return splitlongparameters;
- }
-
- /**
- * 设置对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名)
- * System.setProperty("mail.mime.splitlongparameters", true)
- *
- *
- * @param splitlongparameters 对于超长参数是否切分为多份
- */
- public void setSplitlongparameters(final boolean splitlongparameters) {
- this.splitlongparameters = splitlongparameters;
- }
-
/**
* 对于文件名是否使用{@link #charset}编码,默认为 {@code true}
*
@@ -583,14 +589,15 @@ public class MailAccount implements Serializable {
* @return {@link Properties}
*/
public Properties getSmtpProps() {
- //全局系统参数
- System.setProperty(SPLIT_LONG_PARAMS, String.valueOf(this.splitlongparameters));
-
final Properties p = new Properties();
p.put(MAIL_PROTOCOL, "smtp");
p.put(SMTP_HOST, this.host);
p.put(SMTP_PORT, String.valueOf(this.port));
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) {
p.put(SMTP_TIMEOUT, String.valueOf(this.timeout));
}
@@ -638,7 +645,7 @@ public class MailAccount implements Serializable {
* @return this
*/
public MailAccount defaultIfEmpty() {
- // 去掉发件人的姓名部分
+ Assert.notBlank(this.from, "'from' must not blank!");
final String fromAddress = InternalMailUtil.parseFirstAddress(this.from, this.charset).getAddress();
if (StrUtil.isBlank(this.host)) {
@@ -669,6 +676,6 @@ public class MailAccount implements Serializable {
@Override
public String toString() {
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 + "]";
}
}
diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/mail/Oauth2Test.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/mail/Oauth2Test.java
new file mode 100644
index 000000000..d0009c35e
--- /dev/null
+++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/mail/Oauth2Test.java
@@ -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);
+ }
+}