From 44a796ed57e87a386cf212d6e750343faf9affdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90li?= <15040126243@163.com> Date: Fri, 14 Apr 2023 14:20:57 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E9=82=AE?= =?UTF-8?q?=E4=BB=B6=E5=B7=A5=E5=85=B7=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/mail/utils/GlobalMailAccount.java | 63 +- .../common/mail/utils/InternalMailUtil.java | 165 +-- .../org/dromara/common/mail/utils/Mail.java | 824 +++++------ .../common/mail/utils/MailAccount.java | 1272 ++++++++--------- .../common/mail/utils/MailException.java | 41 +- .../dromara/common/mail/utils/MailUtils.java | 9 +- .../mail/utils/UserPassAuthenticator.java | 32 +- 7 files changed, 1203 insertions(+), 1203 deletions(-) diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/GlobalMailAccount.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/GlobalMailAccount.java index a18f3fec0..fdae86975 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/GlobalMailAccount.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/GlobalMailAccount.java @@ -6,42 +6,41 @@ import cn.hutool.core.io.IORuntimeException; * 全局邮件帐户,依赖于邮件配置文件{@link MailAccount#MAIL_SETTING_PATHS} * * @author looly - * */ public enum GlobalMailAccount { - INSTANCE; + INSTANCE; - private final MailAccount mailAccount; + private final MailAccount mailAccount; - /** - * 构造 - */ - GlobalMailAccount() { - mailAccount = createDefaultAccount(); - } + /** + * 构造 + */ + GlobalMailAccount() { + mailAccount = createDefaultAccount(); + } - /** - * 获得邮件帐户 - * - * @return 邮件帐户 - */ - public MailAccount getAccount() { - return this.mailAccount; - } + /** + * 获得邮件帐户 + * + * @return 邮件帐户 + */ + public MailAccount getAccount() { + return this.mailAccount; + } - /** - * 创建默认帐户 - * - * @return MailAccount - */ - private MailAccount createDefaultAccount() { - for (String mailSettingPath : MailAccount.MAIL_SETTING_PATHS) { - try { - return new MailAccount(mailSettingPath); - } catch (IORuntimeException ignore) { - //ignore - } - } - return null; - } + /** + * 创建默认帐户 + * + * @return MailAccount + */ + private MailAccount createDefaultAccount() { + for (String mailSettingPath : MailAccount.MAIL_SETTING_PATHS) { + try { + return new MailAccount(mailSettingPath); + } catch (IORuntimeException ignore) { + //ignore + } + } + return null; + } } diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/InternalMailUtil.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/InternalMailUtil.java index ca1a498cb..b755e7370 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/InternalMailUtil.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/InternalMailUtil.java @@ -13,95 +13,96 @@ import java.util.List; /** * 邮件内部工具类 + * * @author looly * @since 3.2.3 */ public class InternalMailUtil { - /** - * 将多个字符串邮件地址转为{@link InternetAddress}列表
- * 单个字符串地址可以是多个地址合并的字符串 - * - * @param addrStrs 地址数组 - * @param charset 编码(主要用于中文用户名的编码) - * @return 地址数组 - * @since 4.0.3 - */ - public static InternetAddress[] parseAddressFromStrs(String[] addrStrs, Charset charset) { - final List resultList = new ArrayList<>(addrStrs.length); - InternetAddress[] addrs; - for (String addrStr : addrStrs) { - addrs = parseAddress(addrStr, charset); - if (ArrayUtil.isNotEmpty(addrs)) { - Collections.addAll(resultList, addrs); - } - } - return resultList.toArray(new InternetAddress[0]); - } + /** + * 将多个字符串邮件地址转为{@link InternetAddress}列表
+ * 单个字符串地址可以是多个地址合并的字符串 + * + * @param addrStrs 地址数组 + * @param charset 编码(主要用于中文用户名的编码) + * @return 地址数组 + * @since 4.0.3 + */ + public static InternetAddress[] parseAddressFromStrs(String[] addrStrs, Charset charset) { + final List resultList = new ArrayList<>(addrStrs.length); + InternetAddress[] addrs; + for (String addrStr : addrStrs) { + addrs = parseAddress(addrStr, charset); + if (ArrayUtil.isNotEmpty(addrs)) { + Collections.addAll(resultList, addrs); + } + } + return resultList.toArray(new InternetAddress[0]); + } - /** - * 解析第一个地址 - * - * @param address 地址字符串 - * @param charset 编码,{@code null}表示使用系统属性定义的编码或系统编码 - * @return 地址列表 - */ - public static InternetAddress parseFirstAddress(String address, Charset charset) { - final InternetAddress[] internetAddresses = parseAddress(address, charset); - if (ArrayUtil.isEmpty(internetAddresses)) { - try { - return new InternetAddress(address); - } catch (AddressException e) { - throw new MailException(e); - } - } - return internetAddresses[0]; - } + /** + * 解析第一个地址 + * + * @param address 地址字符串 + * @param charset 编码,{@code null}表示使用系统属性定义的编码或系统编码 + * @return 地址列表 + */ + public static InternetAddress parseFirstAddress(String address, Charset charset) { + final InternetAddress[] internetAddresses = parseAddress(address, charset); + if (ArrayUtil.isEmpty(internetAddresses)) { + try { + return new InternetAddress(address); + } catch (AddressException e) { + throw new MailException(e); + } + } + return internetAddresses[0]; + } - /** - * 将一个地址字符串解析为多个地址
- * 地址间使用" "、","、";"分隔 - * - * @param address 地址字符串 - * @param charset 编码,{@code null}表示使用系统属性定义的编码或系统编码 - * @return 地址列表 - */ - public static InternetAddress[] parseAddress(String address, Charset charset) { - InternetAddress[] addresses; - try { - addresses = InternetAddress.parse(address); - } catch (AddressException e) { - throw new MailException(e); - } - //编码用户名 - if (ArrayUtil.isNotEmpty(addresses)) { - final String charsetStr = null == charset ? null : charset.name(); - for (InternetAddress internetAddress : addresses) { - try { - internetAddress.setPersonal(internetAddress.getPersonal(), charsetStr); - } catch (UnsupportedEncodingException e) { - throw new MailException(e); - } - } - } + /** + * 将一个地址字符串解析为多个地址
+ * 地址间使用" "、","、";"分隔 + * + * @param address 地址字符串 + * @param charset 编码,{@code null}表示使用系统属性定义的编码或系统编码 + * @return 地址列表 + */ + public static InternetAddress[] parseAddress(String address, Charset charset) { + InternetAddress[] addresses; + try { + addresses = InternetAddress.parse(address); + } catch (AddressException e) { + throw new MailException(e); + } + //编码用户名 + if (ArrayUtil.isNotEmpty(addresses)) { + final String charsetStr = null == charset ? null : charset.name(); + for (InternetAddress internetAddress : addresses) { + try { + internetAddress.setPersonal(internetAddress.getPersonal(), charsetStr); + } catch (UnsupportedEncodingException e) { + throw new MailException(e); + } + } + } - return addresses; - } + return addresses; + } - /** - * 编码中文字符
- * 编码失败返回原字符串 - * - * @param text 被编码的文本 - * @param charset 编码 - * @return 编码后的结果 - */ - public static String encodeText(String text, Charset charset) { - try { - return MimeUtility.encodeText(text, charset.name(), null); - } catch (UnsupportedEncodingException e) { - // ignore - } - return text; - } + /** + * 编码中文字符
+ * 编码失败返回原字符串 + * + * @param text 被编码的文本 + * @param charset 编码 + * @return 编码后的结果 + */ + public static String encodeText(String text, Charset charset) { + try { + return MimeUtility.encodeText(text, charset.name(), null); + } catch (UnsupportedEncodingException e) { + // ignore + } + return text; + } } diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/Mail.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/Mail.java index 17459d34a..6ca4b69ec 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/Mail.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/Mail.java @@ -32,452 +32,452 @@ public class Mail implements Builder { @Serial private static final long serialVersionUID = 1L; - /** - * 邮箱帐户信息以及一些客户端配置信息 - */ - private final MailAccount mailAccount; - /** - * 收件人列表 - */ - private String[] tos; - /** - * 抄送人列表(carbon copy) - */ - private String[] ccs; - /** - * 密送人列表(blind carbon copy) - */ - private String[] bccs; - /** - * 回复地址(reply-to) - */ - private String[] reply; - /** - * 标题 - */ - private String title; - /** - * 内容 - */ - private String content; - /** - * 是否为HTML - */ - private boolean isHtml; - /** - * 正文、附件和图片的混合部分 - */ - private final Multipart multipart = new MimeMultipart(); - /** - * 是否使用全局会话,默认为false - */ - private boolean useGlobalSession = false; + /** + * 邮箱帐户信息以及一些客户端配置信息 + */ + private final MailAccount mailAccount; + /** + * 收件人列表 + */ + private String[] tos; + /** + * 抄送人列表(carbon copy) + */ + private String[] ccs; + /** + * 密送人列表(blind carbon copy) + */ + private String[] bccs; + /** + * 回复地址(reply-to) + */ + private String[] reply; + /** + * 标题 + */ + private String title; + /** + * 内容 + */ + private String content; + /** + * 是否为HTML + */ + private boolean isHtml; + /** + * 正文、附件和图片的混合部分 + */ + private final Multipart multipart = new MimeMultipart(); + /** + * 是否使用全局会话,默认为false + */ + private boolean useGlobalSession = false; - /** - * debug输出位置,可以自定义debug日志 - */ - private PrintStream debugOutput; + /** + * debug输出位置,可以自定义debug日志 + */ + private PrintStream debugOutput; - /** - * 创建邮件客户端 - * - * @param mailAccount 邮件帐号 - * @return Mail - */ - public static Mail create(MailAccount mailAccount) { - return new Mail(mailAccount); - } + /** + * 创建邮件客户端 + * + * @param mailAccount 邮件帐号 + * @return Mail + */ + public static Mail create(MailAccount mailAccount) { + return new Mail(mailAccount); + } - /** - * 创建邮件客户端,使用全局邮件帐户 - * - * @return Mail - */ - public static Mail create() { - return new Mail(); - } + /** + * 创建邮件客户端,使用全局邮件帐户 + * + * @return Mail + */ + public static Mail create() { + return new Mail(); + } - // --------------------------------------------------------------- Constructor start + // --------------------------------------------------------------- Constructor start - /** - * 构造,使用全局邮件帐户 - */ - public Mail() { - this(GlobalMailAccount.INSTANCE.getAccount()); - } + /** + * 构造,使用全局邮件帐户 + */ + public Mail() { + this(GlobalMailAccount.INSTANCE.getAccount()); + } - /** - * 构造 - * - * @param mailAccount 邮件帐户,如果为null使用默认配置文件的全局邮件配置 - */ - public Mail(MailAccount mailAccount) { - mailAccount = (null != mailAccount) ? mailAccount : GlobalMailAccount.INSTANCE.getAccount(); - this.mailAccount = mailAccount.defaultIfEmpty(); - } - // --------------------------------------------------------------- Constructor end + /** + * 构造 + * + * @param mailAccount 邮件帐户,如果为null使用默认配置文件的全局邮件配置 + */ + public Mail(MailAccount mailAccount) { + mailAccount = (null != mailAccount) ? mailAccount : GlobalMailAccount.INSTANCE.getAccount(); + this.mailAccount = mailAccount.defaultIfEmpty(); + } + // --------------------------------------------------------------- Constructor end - // --------------------------------------------------------------- Getters and Setters start + // --------------------------------------------------------------- Getters and Setters start - /** - * 设置收件人 - * - * @param tos 收件人列表 - * @return this - * @see #setTos(String...) - */ - public Mail to(String... tos) { - return setTos(tos); - } + /** + * 设置收件人 + * + * @param tos 收件人列表 + * @return this + * @see #setTos(String...) + */ + public Mail to(String... tos) { + return setTos(tos); + } - /** - * 设置多个收件人 - * - * @param tos 收件人列表 - * @return this - */ - public Mail setTos(String... tos) { - this.tos = tos; - return this; - } + /** + * 设置多个收件人 + * + * @param tos 收件人列表 + * @return this + */ + public Mail setTos(String... tos) { + this.tos = tos; + return this; + } - /** - * 设置多个抄送人(carbon copy) - * - * @param ccs 抄送人列表 - * @return this - * @since 4.0.3 - */ - public Mail setCcs(String... ccs) { - this.ccs = ccs; - return this; - } + /** + * 设置多个抄送人(carbon copy) + * + * @param ccs 抄送人列表 + * @return this + * @since 4.0.3 + */ + public Mail setCcs(String... ccs) { + this.ccs = ccs; + return this; + } - /** - * 设置多个密送人(blind carbon copy) - * - * @param bccs 密送人列表 - * @return this - * @since 4.0.3 - */ - public Mail setBccs(String... bccs) { - this.bccs = bccs; - return this; - } + /** + * 设置多个密送人(blind carbon copy) + * + * @param bccs 密送人列表 + * @return this + * @since 4.0.3 + */ + public Mail setBccs(String... bccs) { + this.bccs = bccs; + return this; + } - /** - * 设置多个回复地址(reply-to) - * - * @param reply 回复地址(reply-to)列表 - * @return this - * @since 4.6.0 - */ - public Mail setReply(String... reply) { - this.reply = reply; - return this; - } + /** + * 设置多个回复地址(reply-to) + * + * @param reply 回复地址(reply-to)列表 + * @return this + * @since 4.6.0 + */ + public Mail setReply(String... reply) { + this.reply = reply; + return this; + } - /** - * 设置标题 - * - * @param title 标题 - * @return this - */ - public Mail setTitle(String title) { - this.title = title; - return this; - } + /** + * 设置标题 + * + * @param title 标题 + * @return this + */ + public Mail setTitle(String title) { + this.title = title; + return this; + } - /** - * 设置正文
- * 正文可以是普通文本也可以是HTML(默认普通文本),可以通过调用{@link #setHtml(boolean)} 设置是否为HTML - * - * @param content 正文 - * @return this - */ - public Mail setContent(String content) { - this.content = content; - return this; - } + /** + * 设置正文
+ * 正文可以是普通文本也可以是HTML(默认普通文本),可以通过调用{@link #setHtml(boolean)} 设置是否为HTML + * + * @param content 正文 + * @return this + */ + public Mail setContent(String content) { + this.content = content; + return this; + } - /** - * 设置是否是HTML - * - * @param isHtml 是否为HTML - * @return this - */ - public Mail setHtml(boolean isHtml) { - this.isHtml = isHtml; - return this; - } + /** + * 设置是否是HTML + * + * @param isHtml 是否为HTML + * @return this + */ + public Mail setHtml(boolean isHtml) { + this.isHtml = isHtml; + return this; + } - /** - * 设置正文 - * - * @param content 正文内容 - * @param isHtml 是否为HTML - * @return this - */ - public Mail setContent(String content, boolean isHtml) { - setContent(content); - return setHtml(isHtml); - } + /** + * 设置正文 + * + * @param content 正文内容 + * @param isHtml 是否为HTML + * @return this + */ + public Mail setContent(String content, boolean isHtml) { + setContent(content); + return setHtml(isHtml); + } - /** - * 设置文件类型附件,文件可以是图片文件,此时自动设置cid(正文中引用图片),默认cid为文件名 - * - * @param files 附件文件列表 - * @return this - */ - public Mail setFiles(File... files) { - if (ArrayUtil.isEmpty(files)) { - return this; - } + /** + * 设置文件类型附件,文件可以是图片文件,此时自动设置cid(正文中引用图片),默认cid为文件名 + * + * @param files 附件文件列表 + * @return this + */ + public Mail setFiles(File... files) { + if (ArrayUtil.isEmpty(files)) { + return this; + } - final DataSource[] attachments = new DataSource[files.length]; - for (int i = 0; i < files.length; i++) { - attachments[i] = new FileDataSource(files[i]); - } - return setAttachments(attachments); - } + final DataSource[] attachments = new DataSource[files.length]; + for (int i = 0; i < files.length; i++) { + attachments[i] = new FileDataSource(files[i]); + } + return setAttachments(attachments); + } - /** - * 增加附件或图片,附件使用{@link DataSource} 形式表示,可以使用{@link FileDataSource}包装文件表示文件附件 - * - * @param attachments 附件列表 - * @return this - * @since 4.0.9 - */ - public Mail setAttachments(DataSource... attachments) { - if (ArrayUtil.isNotEmpty(attachments)) { - final Charset charset = this.mailAccount.getCharset(); - MimeBodyPart bodyPart; - String nameEncoded; - try { - for (DataSource attachment : attachments) { - bodyPart = new MimeBodyPart(); - bodyPart.setDataHandler(new DataHandler(attachment)); - nameEncoded = attachment.getName(); - if (this.mailAccount.isEncodefilename()) { - nameEncoded = InternalMailUtil.encodeText(nameEncoded, charset); - } - // 普通附件文件名 - bodyPart.setFileName(nameEncoded); - if (StrUtil.startWith(attachment.getContentType(), "image/")) { - // 图片附件,用于正文中引用图片 - bodyPart.setContentID(nameEncoded); - } - this.multipart.addBodyPart(bodyPart); - } - } catch (MessagingException e) { - throw new MailException(e); - } - } - return this; - } + /** + * 增加附件或图片,附件使用{@link DataSource} 形式表示,可以使用{@link FileDataSource}包装文件表示文件附件 + * + * @param attachments 附件列表 + * @return this + * @since 4.0.9 + */ + public Mail setAttachments(DataSource... attachments) { + if (ArrayUtil.isNotEmpty(attachments)) { + final Charset charset = this.mailAccount.getCharset(); + MimeBodyPart bodyPart; + String nameEncoded; + try { + for (DataSource attachment : attachments) { + bodyPart = new MimeBodyPart(); + bodyPart.setDataHandler(new DataHandler(attachment)); + nameEncoded = attachment.getName(); + if (this.mailAccount.isEncodefilename()) { + nameEncoded = InternalMailUtil.encodeText(nameEncoded, charset); + } + // 普通附件文件名 + bodyPart.setFileName(nameEncoded); + if (StrUtil.startWith(attachment.getContentType(), "image/")) { + // 图片附件,用于正文中引用图片 + bodyPart.setContentID(nameEncoded); + } + this.multipart.addBodyPart(bodyPart); + } + } catch (MessagingException e) { + throw new MailException(e); + } + } + return this; + } - /** - * 增加图片,图片的键对应到邮件模板中的占位字符串,图片类型默认为"image/jpeg" - * - * @param cid 图片与占位符,占位符格式为cid:${cid} - * @param imageStream 图片文件 - * @return this - * @since 4.6.3 - */ - public Mail addImage(String cid, InputStream imageStream) { - return addImage(cid, imageStream, null); - } + /** + * 增加图片,图片的键对应到邮件模板中的占位字符串,图片类型默认为"image/jpeg" + * + * @param cid 图片与占位符,占位符格式为cid:${cid} + * @param imageStream 图片文件 + * @return this + * @since 4.6.3 + */ + public Mail addImage(String cid, InputStream imageStream) { + return addImage(cid, imageStream, null); + } - /** - * 增加图片,图片的键对应到邮件模板中的占位字符串 - * - * @param cid 图片与占位符,占位符格式为cid:${cid} - * @param imageStream 图片流,不关闭 - * @param contentType 图片类型,null赋值默认的"image/jpeg" - * @return this - * @since 4.6.3 - */ - public Mail addImage(String cid, InputStream imageStream, String contentType) { - ByteArrayDataSource imgSource; - try { - imgSource = new ByteArrayDataSource(imageStream, ObjectUtil.defaultIfNull(contentType, "image/jpeg")); - } catch (IOException e) { - throw new IORuntimeException(e); - } - imgSource.setName(cid); - return setAttachments(imgSource); - } + /** + * 增加图片,图片的键对应到邮件模板中的占位字符串 + * + * @param cid 图片与占位符,占位符格式为cid:${cid} + * @param imageStream 图片流,不关闭 + * @param contentType 图片类型,null赋值默认的"image/jpeg" + * @return this + * @since 4.6.3 + */ + public Mail addImage(String cid, InputStream imageStream, String contentType) { + ByteArrayDataSource imgSource; + try { + imgSource = new ByteArrayDataSource(imageStream, ObjectUtil.defaultIfNull(contentType, "image/jpeg")); + } catch (IOException e) { + throw new IORuntimeException(e); + } + imgSource.setName(cid); + return setAttachments(imgSource); + } - /** - * 增加图片,图片的键对应到邮件模板中的占位字符串 - * - * @param cid 图片与占位符,占位符格式为cid:${cid} - * @param imageFile 图片文件 - * @return this - * @since 4.6.3 - */ - public Mail addImage(String cid, File imageFile) { - InputStream in = null; - try { - in = FileUtil.getInputStream(imageFile); - return addImage(cid, in, FileTypeMap.getDefaultFileTypeMap().getContentType(imageFile)); - } finally { - IoUtil.close(in); - } - } + /** + * 增加图片,图片的键对应到邮件模板中的占位字符串 + * + * @param cid 图片与占位符,占位符格式为cid:${cid} + * @param imageFile 图片文件 + * @return this + * @since 4.6.3 + */ + public Mail addImage(String cid, File imageFile) { + InputStream in = null; + try { + in = FileUtil.getInputStream(imageFile); + return addImage(cid, in, FileTypeMap.getDefaultFileTypeMap().getContentType(imageFile)); + } finally { + IoUtil.close(in); + } + } - /** - * 设置字符集编码 - * - * @param charset 字符集编码 - * @return this - * @see MailAccount#setCharset(Charset) - */ - public Mail setCharset(Charset charset) { - this.mailAccount.setCharset(charset); - return this; - } + /** + * 设置字符集编码 + * + * @param charset 字符集编码 + * @return this + * @see MailAccount#setCharset(Charset) + */ + public Mail setCharset(Charset charset) { + this.mailAccount.setCharset(charset); + return this; + } - /** - * 设置是否使用全局会话,默认为true - * - * @param isUseGlobalSession 是否使用全局会话,默认为true - * @return this - * @since 4.0.2 - */ - public Mail setUseGlobalSession(boolean isUseGlobalSession) { - this.useGlobalSession = isUseGlobalSession; - return this; - } + /** + * 设置是否使用全局会话,默认为true + * + * @param isUseGlobalSession 是否使用全局会话,默认为true + * @return this + * @since 4.0.2 + */ + public Mail setUseGlobalSession(boolean isUseGlobalSession) { + this.useGlobalSession = isUseGlobalSession; + return this; + } - /** - * 设置debug输出位置,可以自定义debug日志 - * - * @param debugOutput debug输出位置 - * @return this - * @since 5.5.6 - */ - public Mail setDebugOutput(PrintStream debugOutput) { - this.debugOutput = debugOutput; - return this; - } - // --------------------------------------------------------------- Getters and Setters end + /** + * 设置debug输出位置,可以自定义debug日志 + * + * @param debugOutput debug输出位置 + * @return this + * @since 5.5.6 + */ + public Mail setDebugOutput(PrintStream debugOutput) { + this.debugOutput = debugOutput; + return this; + } + // --------------------------------------------------------------- Getters and Setters end - @Override - public MimeMessage build() { - try { - return buildMsg(); - } catch (MessagingException e) { - throw new MailException(e); - } - } + @Override + public MimeMessage build() { + try { + return buildMsg(); + } catch (MessagingException e) { + throw new MailException(e); + } + } - /** - * 发送 - * - * @return message-id - * @throws MailException 邮件发送异常 - */ - public String send() throws MailException { - try { - return doSend(); - } catch (MessagingException e) { - if (e instanceof SendFailedException) { - // 当地址无效时,显示更加详细的无效地址信息 - final Address[] invalidAddresses = ((SendFailedException) e).getInvalidAddresses(); - final String msg = StrUtil.format("Invalid Addresses: {}", ArrayUtil.toString(invalidAddresses)); - throw new MailException(msg, e); - } - throw new MailException(e); - } - } + /** + * 发送 + * + * @return message-id + * @throws MailException 邮件发送异常 + */ + public String send() throws MailException { + try { + return doSend(); + } catch (MessagingException e) { + if (e instanceof SendFailedException) { + // 当地址无效时,显示更加详细的无效地址信息 + final Address[] invalidAddresses = ((SendFailedException) e).getInvalidAddresses(); + final String msg = StrUtil.format("Invalid Addresses: {}", ArrayUtil.toString(invalidAddresses)); + throw new MailException(msg, e); + } + throw new MailException(e); + } + } - // --------------------------------------------------------------- Private method start + // --------------------------------------------------------------- Private method start - /** - * 执行发送 - * - * @return message-id - * @throws MessagingException 发送异常 - */ - private String doSend() throws MessagingException { - final MimeMessage mimeMessage = buildMsg(); - Transport.send(mimeMessage); - return mimeMessage.getMessageID(); - } + /** + * 执行发送 + * + * @return message-id + * @throws MessagingException 发送异常 + */ + private String doSend() throws MessagingException { + final MimeMessage mimeMessage = buildMsg(); + Transport.send(mimeMessage); + return mimeMessage.getMessageID(); + } - /** - * 构建消息 - * - * @return {@link MimeMessage}消息 - * @throws MessagingException 消息异常 - */ - private MimeMessage buildMsg() throws MessagingException { - final Charset charset = this.mailAccount.getCharset(); - final MimeMessage msg = new MimeMessage(getSession()); - // 发件人 - final String from = this.mailAccount.getFrom(); - if (StrUtil.isEmpty(from)) { - // 用户未提供发送方,则从Session中自动获取 - msg.setFrom(); - } else { - msg.setFrom(InternalMailUtil.parseFirstAddress(from, charset)); - } - // 标题 - msg.setSubject(this.title, (null == charset) ? null : charset.name()); - // 发送时间 - msg.setSentDate(new Date()); - // 内容和附件 - msg.setContent(buildContent(charset)); - // 收件人 - msg.setRecipients(MimeMessage.RecipientType.TO, InternalMailUtil.parseAddressFromStrs(this.tos, charset)); - // 抄送人 - if (ArrayUtil.isNotEmpty(this.ccs)) { - msg.setRecipients(MimeMessage.RecipientType.CC, InternalMailUtil.parseAddressFromStrs(this.ccs, charset)); - } - // 密送人 - if (ArrayUtil.isNotEmpty(this.bccs)) { - msg.setRecipients(MimeMessage.RecipientType.BCC, InternalMailUtil.parseAddressFromStrs(this.bccs, charset)); - } - // 回复地址(reply-to) - if (ArrayUtil.isNotEmpty(this.reply)) { - msg.setReplyTo(InternalMailUtil.parseAddressFromStrs(this.reply, charset)); - } + /** + * 构建消息 + * + * @return {@link MimeMessage}消息 + * @throws MessagingException 消息异常 + */ + private MimeMessage buildMsg() throws MessagingException { + final Charset charset = this.mailAccount.getCharset(); + final MimeMessage msg = new MimeMessage(getSession()); + // 发件人 + final String from = this.mailAccount.getFrom(); + if (StrUtil.isEmpty(from)) { + // 用户未提供发送方,则从Session中自动获取 + msg.setFrom(); + } else { + msg.setFrom(InternalMailUtil.parseFirstAddress(from, charset)); + } + // 标题 + msg.setSubject(this.title, (null == charset) ? null : charset.name()); + // 发送时间 + msg.setSentDate(new Date()); + // 内容和附件 + msg.setContent(buildContent(charset)); + // 收件人 + msg.setRecipients(MimeMessage.RecipientType.TO, InternalMailUtil.parseAddressFromStrs(this.tos, charset)); + // 抄送人 + if (ArrayUtil.isNotEmpty(this.ccs)) { + msg.setRecipients(MimeMessage.RecipientType.CC, InternalMailUtil.parseAddressFromStrs(this.ccs, charset)); + } + // 密送人 + if (ArrayUtil.isNotEmpty(this.bccs)) { + msg.setRecipients(MimeMessage.RecipientType.BCC, InternalMailUtil.parseAddressFromStrs(this.bccs, charset)); + } + // 回复地址(reply-to) + if (ArrayUtil.isNotEmpty(this.reply)) { + msg.setReplyTo(InternalMailUtil.parseAddressFromStrs(this.reply, charset)); + } - return msg; - } + return msg; + } - /** - * 构建邮件信息主体 - * - * @param charset 编码,{@code null}则使用{@link MimeUtility#getDefaultJavaCharset()} - * @return 邮件信息主体 - * @throws MessagingException 消息异常 - */ - private Multipart buildContent(Charset charset) throws MessagingException { - final String charsetStr = null != charset ? charset.name() : MimeUtility.getDefaultJavaCharset(); - // 正文 - final MimeBodyPart body = new MimeBodyPart(); - body.setContent(content, StrUtil.format("text/{}; charset={}", isHtml ? "html" : "plain", charsetStr)); - this.multipart.addBodyPart(body); + /** + * 构建邮件信息主体 + * + * @param charset 编码,{@code null}则使用{@link MimeUtility#getDefaultJavaCharset()} + * @return 邮件信息主体 + * @throws MessagingException 消息异常 + */ + private Multipart buildContent(Charset charset) throws MessagingException { + final String charsetStr = null != charset ? charset.name() : MimeUtility.getDefaultJavaCharset(); + // 正文 + final MimeBodyPart body = new MimeBodyPart(); + body.setContent(content, StrUtil.format("text/{}; charset={}", isHtml ? "html" : "plain", charsetStr)); + this.multipart.addBodyPart(body); - return this.multipart; - } + return this.multipart; + } - /** - * 获取默认邮件会话
- * 如果为全局单例的会话,则全局只允许一个邮件帐号,否则每次发送邮件会新建一个新的会话 - * - * @return 邮件会话 {@link Session} - */ - private Session getSession() { - final Session session = MailUtils.getSession(this.mailAccount, this.useGlobalSession); + /** + * 获取默认邮件会话
+ * 如果为全局单例的会话,则全局只允许一个邮件帐号,否则每次发送邮件会新建一个新的会话 + * + * @return 邮件会话 {@link Session} + */ + private Session getSession() { + final Session session = MailUtils.getSession(this.mailAccount, this.useGlobalSession); - if (null != this.debugOutput) { - session.setDebugOut(debugOutput); - } + if (null != this.debugOutput) { + session.setDebugOut(debugOutput); + } - return session; - } - // --------------------------------------------------------------- Private method end + return session; + } + // --------------------------------------------------------------- Private method end } diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailAccount.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailAccount.java index d2b9e2c07..2a732a1a9 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailAccount.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailAccount.java @@ -18,642 +18,642 @@ import java.util.Properties; * @author Luxiaolei */ public class MailAccount implements Serializable { - @Serial + @Serial private static final long serialVersionUID = -6937313421815719204L; - private static final String MAIL_PROTOCOL = "mail.transport.protocol"; - 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"; - 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"; - - // SSL - private static final String STARTTLS_ENABLE = "mail.smtp.starttls.enable"; - private static final String SSL_ENABLE = "mail.smtp.ssl.enable"; - private static final String SSL_PROTOCOLS = "mail.smtp.ssl.protocols"; - private static final String SOCKET_FACTORY = "mail.smtp.socketFactory.class"; - 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 ENCODE_FILE_NAME = "mail.mime.encodefilename"; - //private static final String CHARSET = "mail.mime.charset"; - - // 其他 - private static final String MAIL_DEBUG = "mail.debug"; - - public static final String[] MAIL_SETTING_PATHS = new String[]{"config/mail.setting", "config/mailAccount.setting", "mail.setting"}; - - /** - * SMTP服务器域名 - */ - private String host; - /** - * SMTP服务端口 - */ - private Integer port; - /** - * 是否需要用户名密码验证 - */ - private Boolean auth; - /** - * 用户名 - */ - private String user; - /** - * 密码 - */ - private String pass; - /** - * 发送方,遵循RFC-822标准 - */ - private String from; - - /** - * 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 - */ - private boolean debug; - /** - * 编码用于编码邮件正文和发送人、收件人等中文 - */ - private Charset charset = CharsetUtil.CHARSET_UTF_8; - /** - * 对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名) - */ - private boolean splitlongparameters = false; - /** - * 对于文件名是否使用{@link #charset}编码,默认为 {@code true} - */ - private boolean encodefilename = true; - - /** - * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 - */ - private boolean starttlsEnable = false; - /** - * 使用 SSL安全连接 - */ - private Boolean sslEnable; - - /** - * SSL协议,多个协议用空格分隔 - */ - private String sslProtocols; - - /** - * 指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 - */ - private String socketFactoryClass = "javax.net.ssl.SSLSocketFactory"; - /** - * 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true - */ - private boolean socketFactoryFallback; - /** - * 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 - */ - private int socketFactoryPort = 465; - - /** - * SMTP超时时长,单位毫秒,缺省值不超时 - */ - private long timeout; - /** - * Socket连接超时值,单位毫秒,缺省值不超时 - */ - private long connectionTimeout; - /** - * Socket写出超时值,单位毫秒,缺省值不超时 - */ - private long writeTimeout; - - /** - * 自定义的其他属性,此自定义属性会覆盖默认属性 - */ - private final Map customProperty = new HashMap<>(); - - // -------------------------------------------------------------- Constructor start - - /** - * 构造,所有参数需自行定义或保持默认值 - */ - public MailAccount() { - } - - /** - * 构造 - * - * @param settingPath 配置文件路径 - */ - public MailAccount(String settingPath) { - this(new Setting(settingPath)); - } - - /** - * 构造 - * - * @param setting 配置文件 - */ - public MailAccount(Setting setting) { - setting.toBean(this); - } - - // -------------------------------------------------------------- Constructor end - - /** - * 获得SMTP服务器域名 - * - * @return SMTP服务器域名 - */ - public String getHost() { - return host; - } - - /** - * 设置SMTP服务器域名 - * - * @param host SMTP服务器域名 - * @return this - */ - public MailAccount setHost(String host) { - this.host = host; - return this; - } - - /** - * 获得SMTP服务端口 - * - * @return SMTP服务端口 - */ - public Integer getPort() { - return port; - } - - /** - * 设置SMTP服务端口 - * - * @param port SMTP服务端口 - * @return this - */ - public MailAccount setPort(Integer port) { - this.port = port; - return this; - } - - /** - * 是否需要用户名密码验证 - * - * @return 是否需要用户名密码验证 - */ - public Boolean isAuth() { - return auth; - } - - /** - * 设置是否需要用户名密码验证 - * - * @param isAuth 是否需要用户名密码验证 - * @return this - */ - public MailAccount setAuth(boolean isAuth) { - this.auth = isAuth; - return this; - } - - /** - * 获取用户名 - * - * @return 用户名 - */ - public String getUser() { - return user; - } - - /** - * 设置用户名 - * - * @param user 用户名 - * @return this - */ - public MailAccount setUser(String user) { - this.user = user; - return this; - } - - /** - * 获取密码 - * - * @return 密码 - */ - public String getPass() { - return pass; - } - - /** - * 设置密码 - * - * @param pass 密码 - * @return this - */ - public MailAccount setPass(String pass) { - this.pass = pass; - return this; - } - - /** - * 获取发送方,遵循RFC-822标准 - * - * @return 发送方,遵循RFC-822标准 - */ - public String getFrom() { - return from; - } - - /** - * 设置发送方,遵循RFC-822标准
- * 发件人可以是以下形式: - * - *
-	 * 1. user@xxx.xx
-	 * 2.  name <user@xxx.xx>
-	 * 
- * - * @param from 发送方,遵循RFC-822标准 - * @return this - */ - public MailAccount setFrom(String from) { - this.from = from; - return this; - } - - /** - * 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 - * - * @return 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 - * @since 4.0.2 - */ - public boolean isDebug() { - return debug; - } - - /** - * 设置是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 - * - * @param debug 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 - * @return this - * @since 4.0.2 - */ - public MailAccount setDebug(boolean debug) { - this.debug = debug; - return this; - } - - /** - * 获取字符集编码 - * - * @return 编码,可能为{@code null} - */ - public Charset getCharset() { - return charset; - } - - /** - * 设置字符集编码,此选项不会修改全局配置,若修改全局配置,请设置此项为{@code null}并设置: - *
-	 * 	System.setProperty("mail.mime.charset", charset);
-	 * 
- * - * @param charset 字符集编码,{@code null} 则表示使用全局设置的默认编码,全局编码为mail.mime.charset系统属性 - * @return this - */ - public MailAccount setCharset(Charset charset) { - this.charset = charset; - return this; - } - - /** - * 对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名) - * - * @return 对于超长参数是否切分为多份 - */ - public boolean isSplitlongparameters() { - return splitlongparameters; - } - - /** - * 设置对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名)
- * 注意此项为全局设置,此项会调用 - *
-	 * System.setProperty("mail.mime.splitlongparameters", true)
-	 * 
- * - * @param splitlongparameters 对于超长参数是否切分为多份 - */ - public void setSplitlongparameters(boolean splitlongparameters) { - this.splitlongparameters = splitlongparameters; - } - - /** - * 对于文件名是否使用{@link #charset}编码,默认为 {@code true} - * - * @return 对于文件名是否使用{@link #charset}编码,默认为 {@code true} - * @since 5.7.16 - */ - public boolean isEncodefilename() { - - return encodefilename; - } - - /** - * 设置对于文件名是否使用{@link #charset}编码,此选项不会修改全局配置
- * 如果此选项设置为{@code false},则是否编码取决于两个系统属性: - *
    - *
  • mail.mime.encodefilename 是否编码附件文件名
  • - *
  • mail.mime.charset 编码文件名的编码
  • - *
- * - * @param encodefilename 对于文件名是否使用{@link #charset}编码 - * @since 5.7.16 - */ - public void setEncodefilename(boolean encodefilename) { - this.encodefilename = encodefilename; - } - - /** - * 是否使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 - * - * @return 是否使用 STARTTLS安全连接 - */ - public boolean isStarttlsEnable() { - return this.starttlsEnable; - } - - /** - * 设置是否使用STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 - * - * @param startttlsEnable 是否使用STARTTLS安全连接 - * @return this - */ - public MailAccount setStarttlsEnable(boolean startttlsEnable) { - this.starttlsEnable = startttlsEnable; - return this; - } - - /** - * 是否使用 SSL安全连接 - * - * @return 是否使用 SSL安全连接 - */ - public Boolean isSslEnable() { - return this.sslEnable; - } - - /** - * 设置是否使用SSL安全连接 - * - * @param sslEnable 是否使用SSL安全连接 - * @return this - */ - public MailAccount setSslEnable(Boolean sslEnable) { - this.sslEnable = sslEnable; - return this; - } - - /** - * 获取SSL协议,多个协议用空格分隔 - * - * @return SSL协议,多个协议用空格分隔 - * @since 5.5.7 - */ - public String getSslProtocols() { - return sslProtocols; - } - - /** - * 设置SSL协议,多个协议用空格分隔 - * - * @param sslProtocols SSL协议,多个协议用空格分隔 - * @since 5.5.7 - */ - public void setSslProtocols(String sslProtocols) { - this.sslProtocols = sslProtocols; - } - - /** - * 获取指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 - * - * @return 指定实现javax.net.SocketFactory接口的类的名称, 这个类将被用于创建SMTP的套接字 - */ - public String getSocketFactoryClass() { - return socketFactoryClass; - } - - /** - * 设置指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 - * - * @param socketFactoryClass 指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 - * @return this - */ - public MailAccount setSocketFactoryClass(String socketFactoryClass) { - this.socketFactoryClass = socketFactoryClass; - return this; - } - - /** - * 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true - * - * @return 如果设置为true, 未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true - */ - public boolean isSocketFactoryFallback() { - return socketFactoryFallback; - } - - /** - * 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true - * - * @param socketFactoryFallback 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true - * @return this - */ - public MailAccount setSocketFactoryFallback(boolean socketFactoryFallback) { - this.socketFactoryFallback = socketFactoryFallback; - return this; - } - - /** - * 获取指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 - * - * @return 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 - */ - public int getSocketFactoryPort() { - return socketFactoryPort; - } - - /** - * 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 - * - * @param socketFactoryPort 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 - * @return this - */ - public MailAccount setSocketFactoryPort(int socketFactoryPort) { - this.socketFactoryPort = socketFactoryPort; - return this; - } - - /** - * 设置SMTP超时时长,单位毫秒,缺省值不超时 - * - * @param timeout SMTP超时时长,单位毫秒,缺省值不超时 - * @return this - * @since 4.1.17 - */ - public MailAccount setTimeout(long timeout) { - this.timeout = timeout; - return this; - } - - /** - * 设置Socket连接超时值,单位毫秒,缺省值不超时 - * - * @param connectionTimeout Socket连接超时值,单位毫秒,缺省值不超时 - * @return this - * @since 4.1.17 - */ - public MailAccount setConnectionTimeout(long connectionTimeout) { - this.connectionTimeout = connectionTimeout; - return this; - } - - /** - * 设置Socket写出超时值,单位毫秒,缺省值不超时 - * - * @param writeTimeout Socket写出超时值,单位毫秒,缺省值不超时 - * @return this - * @since 5.8.3 - */ - public MailAccount setWriteTimeout(long writeTimeout) { - this.writeTimeout = writeTimeout; - return this; - } - - /** - * 获取自定义属性列表 - * - * @return 自定义参数列表 - * @since 5.6.4 - */ - public Map getCustomProperty() { - return customProperty; - } - - /** - * 设置自定义属性,如mail.smtp.ssl.socketFactory - * - * @param key 属性名,空白被忽略 - * @param value 属性值, null被忽略 - * @return this - * @since 5.6.4 - */ - public MailAccount setCustomProperty(String key, Object value) { - if (StrUtil.isNotBlank(key) && ObjectUtil.isNotNull(value)) { - this.customProperty.put(key, value); - } - return this; - } - - /** - * 获得SMTP相关信息 - * - * @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)); - if (this.timeout > 0) { - p.put(SMTP_TIMEOUT, String.valueOf(this.timeout)); - } - if (this.connectionTimeout > 0) { - p.put(SMTP_CONNECTION_TIMEOUT, String.valueOf(this.connectionTimeout)); - } - // issue#2355 - if (this.writeTimeout > 0) { - p.put(SMTP_WRITE_TIMEOUT, String.valueOf(this.writeTimeout)); - } - - p.put(MAIL_DEBUG, String.valueOf(this.debug)); - - if (this.starttlsEnable) { - //STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 - p.put(STARTTLS_ENABLE, "true"); - - if (null == this.sslEnable) { - //为了兼容旧版本,当用户没有此项配置时,按照starttlsEnable开启状态时对待 - this.sslEnable = true; - } - } - - // SSL - if (null != this.sslEnable && this.sslEnable) { - p.put(SSL_ENABLE, "true"); - p.put(SOCKET_FACTORY, socketFactoryClass); - p.put(SOCKET_FACTORY_FALLBACK, String.valueOf(this.socketFactoryFallback)); - p.put(SOCKET_FACTORY_PORT, String.valueOf(this.socketFactoryPort)); - // issue#IZN95@Gitee,在Linux下需自定义SSL协议版本 - if (StrUtil.isNotBlank(this.sslProtocols)) { - p.put(SSL_PROTOCOLS, this.sslProtocols); - } - } - - // 补充自定义属性,允许自定属性覆盖已经设置的值 - p.putAll(this.customProperty); - - return p; - } - - /** - * 如果某些值为null,使用默认值 - * - * @return this - */ - public MailAccount defaultIfEmpty() { - // 去掉发件人的姓名部分 - final String fromAddress = InternalMailUtil.parseFirstAddress(this.from, this.charset).getAddress(); - - if (StrUtil.isBlank(this.host)) { - // 如果SMTP地址为空,默认使用smtp.<发件人邮箱后缀> - this.host = StrUtil.format("smtp.{}", StrUtil.subSuf(fromAddress, fromAddress.indexOf('@') + 1)); - } - if (StrUtil.isBlank(user)) { - // 如果用户名为空,默认为发件人(issue#I4FYVY@Gitee) - //this.user = StrUtil.subPre(fromAddress, fromAddress.indexOf('@')); - this.user = fromAddress; - } - if (null == this.auth) { - // 如果密码非空白,则使用认证模式 - this.auth = (false == StrUtil.isBlank(this.pass)); - } - if (null == this.port) { - // 端口在SSL状态下默认与socketFactoryPort一致,非SSL状态下默认为25 - this.port = (null != this.sslEnable && this.sslEnable) ? this.socketFactoryPort : 25; - } - if (null == this.charset) { - // 默认UTF-8编码 - this.charset = CharsetUtil.CHARSET_UTF_8; - } - - return this; - } - - @Override - public String toString() { - return "MailAccount [host=" + host + ", port=" + port + ", auth=" + auth + ", user=" + user + ", pass=" + (StrUtil.isEmpty(this.pass) ? "" : "******") + ", from=" + from + ", startttlsEnable=" - + starttlsEnable + ", socketFactoryClass=" + socketFactoryClass + ", socketFactoryFallback=" + socketFactoryFallback + ", socketFactoryPort=" + socketFactoryPort + "]"; - } + private static final String MAIL_PROTOCOL = "mail.transport.protocol"; + 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"; + 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"; + + // SSL + private static final String STARTTLS_ENABLE = "mail.smtp.starttls.enable"; + private static final String SSL_ENABLE = "mail.smtp.ssl.enable"; + private static final String SSL_PROTOCOLS = "mail.smtp.ssl.protocols"; + private static final String SOCKET_FACTORY = "mail.smtp.socketFactory.class"; + 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 ENCODE_FILE_NAME = "mail.mime.encodefilename"; + //private static final String CHARSET = "mail.mime.charset"; + + // 其他 + private static final String MAIL_DEBUG = "mail.debug"; + + public static final String[] MAIL_SETTING_PATHS = new String[]{"config/mail.setting", "config/mailAccount.setting", "mail.setting"}; + + /** + * SMTP服务器域名 + */ + private String host; + /** + * SMTP服务端口 + */ + private Integer port; + /** + * 是否需要用户名密码验证 + */ + private Boolean auth; + /** + * 用户名 + */ + private String user; + /** + * 密码 + */ + private String pass; + /** + * 发送方,遵循RFC-822标准 + */ + private String from; + + /** + * 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 + */ + private boolean debug; + /** + * 编码用于编码邮件正文和发送人、收件人等中文 + */ + private Charset charset = CharsetUtil.CHARSET_UTF_8; + /** + * 对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名) + */ + private boolean splitlongparameters = false; + /** + * 对于文件名是否使用{@link #charset}编码,默认为 {@code true} + */ + private boolean encodefilename = true; + + /** + * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + */ + private boolean starttlsEnable = false; + /** + * 使用 SSL安全连接 + */ + private Boolean sslEnable; + + /** + * SSL协议,多个协议用空格分隔 + */ + private String sslProtocols; + + /** + * 指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 + */ + private String socketFactoryClass = "javax.net.ssl.SSLSocketFactory"; + /** + * 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true + */ + private boolean socketFactoryFallback; + /** + * 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 + */ + private int socketFactoryPort = 465; + + /** + * SMTP超时时长,单位毫秒,缺省值不超时 + */ + private long timeout; + /** + * Socket连接超时值,单位毫秒,缺省值不超时 + */ + private long connectionTimeout; + /** + * Socket写出超时值,单位毫秒,缺省值不超时 + */ + private long writeTimeout; + + /** + * 自定义的其他属性,此自定义属性会覆盖默认属性 + */ + private final Map customProperty = new HashMap<>(); + + // -------------------------------------------------------------- Constructor start + + /** + * 构造,所有参数需自行定义或保持默认值 + */ + public MailAccount() { + } + + /** + * 构造 + * + * @param settingPath 配置文件路径 + */ + public MailAccount(String settingPath) { + this(new Setting(settingPath)); + } + + /** + * 构造 + * + * @param setting 配置文件 + */ + public MailAccount(Setting setting) { + setting.toBean(this); + } + + // -------------------------------------------------------------- Constructor end + + /** + * 获得SMTP服务器域名 + * + * @return SMTP服务器域名 + */ + public String getHost() { + return host; + } + + /** + * 设置SMTP服务器域名 + * + * @param host SMTP服务器域名 + * @return this + */ + public MailAccount setHost(String host) { + this.host = host; + return this; + } + + /** + * 获得SMTP服务端口 + * + * @return SMTP服务端口 + */ + public Integer getPort() { + return port; + } + + /** + * 设置SMTP服务端口 + * + * @param port SMTP服务端口 + * @return this + */ + public MailAccount setPort(Integer port) { + this.port = port; + return this; + } + + /** + * 是否需要用户名密码验证 + * + * @return 是否需要用户名密码验证 + */ + public Boolean isAuth() { + return auth; + } + + /** + * 设置是否需要用户名密码验证 + * + * @param isAuth 是否需要用户名密码验证 + * @return this + */ + public MailAccount setAuth(boolean isAuth) { + this.auth = isAuth; + return this; + } + + /** + * 获取用户名 + * + * @return 用户名 + */ + public String getUser() { + return user; + } + + /** + * 设置用户名 + * + * @param user 用户名 + * @return this + */ + public MailAccount setUser(String user) { + this.user = user; + return this; + } + + /** + * 获取密码 + * + * @return 密码 + */ + public String getPass() { + return pass; + } + + /** + * 设置密码 + * + * @param pass 密码 + * @return this + */ + public MailAccount setPass(String pass) { + this.pass = pass; + return this; + } + + /** + * 获取发送方,遵循RFC-822标准 + * + * @return 发送方,遵循RFC-822标准 + */ + public String getFrom() { + return from; + } + + /** + * 设置发送方,遵循RFC-822标准
+ * 发件人可以是以下形式: + * + *
+     * 1. user@xxx.xx
+     * 2.  name <user@xxx.xx>
+     * 
+ * + * @param from 发送方,遵循RFC-822标准 + * @return this + */ + public MailAccount setFrom(String from) { + this.from = from; + return this; + } + + /** + * 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 + * + * @return 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 + * @since 4.0.2 + */ + public boolean isDebug() { + return debug; + } + + /** + * 设置是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 + * + * @param debug 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 + * @return this + * @since 4.0.2 + */ + public MailAccount setDebug(boolean debug) { + this.debug = debug; + return this; + } + + /** + * 获取字符集编码 + * + * @return 编码,可能为{@code null} + */ + public Charset getCharset() { + return charset; + } + + /** + * 设置字符集编码,此选项不会修改全局配置,若修改全局配置,请设置此项为{@code null}并设置: + *
+     * 	System.setProperty("mail.mime.charset", charset);
+     * 
+ * + * @param charset 字符集编码,{@code null} 则表示使用全局设置的默认编码,全局编码为mail.mime.charset系统属性 + * @return this + */ + public MailAccount setCharset(Charset charset) { + this.charset = charset; + return this; + } + + /** + * 对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名) + * + * @return 对于超长参数是否切分为多份 + */ + public boolean isSplitlongparameters() { + return splitlongparameters; + } + + /** + * 设置对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名)
+ * 注意此项为全局设置,此项会调用 + *
+     * System.setProperty("mail.mime.splitlongparameters", true)
+     * 
+ * + * @param splitlongparameters 对于超长参数是否切分为多份 + */ + public void setSplitlongparameters(boolean splitlongparameters) { + this.splitlongparameters = splitlongparameters; + } + + /** + * 对于文件名是否使用{@link #charset}编码,默认为 {@code true} + * + * @return 对于文件名是否使用{@link #charset}编码,默认为 {@code true} + * @since 5.7.16 + */ + public boolean isEncodefilename() { + + return encodefilename; + } + + /** + * 设置对于文件名是否使用{@link #charset}编码,此选项不会修改全局配置
+ * 如果此选项设置为{@code false},则是否编码取决于两个系统属性: + *
    + *
  • mail.mime.encodefilename 是否编码附件文件名
  • + *
  • mail.mime.charset 编码文件名的编码
  • + *
+ * + * @param encodefilename 对于文件名是否使用{@link #charset}编码 + * @since 5.7.16 + */ + public void setEncodefilename(boolean encodefilename) { + this.encodefilename = encodefilename; + } + + /** + * 是否使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + * + * @return 是否使用 STARTTLS安全连接 + */ + public boolean isStarttlsEnable() { + return this.starttlsEnable; + } + + /** + * 设置是否使用STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + * + * @param startttlsEnable 是否使用STARTTLS安全连接 + * @return this + */ + public MailAccount setStarttlsEnable(boolean startttlsEnable) { + this.starttlsEnable = startttlsEnable; + return this; + } + + /** + * 是否使用 SSL安全连接 + * + * @return 是否使用 SSL安全连接 + */ + public Boolean isSslEnable() { + return this.sslEnable; + } + + /** + * 设置是否使用SSL安全连接 + * + * @param sslEnable 是否使用SSL安全连接 + * @return this + */ + public MailAccount setSslEnable(Boolean sslEnable) { + this.sslEnable = sslEnable; + return this; + } + + /** + * 获取SSL协议,多个协议用空格分隔 + * + * @return SSL协议,多个协议用空格分隔 + * @since 5.5.7 + */ + public String getSslProtocols() { + return sslProtocols; + } + + /** + * 设置SSL协议,多个协议用空格分隔 + * + * @param sslProtocols SSL协议,多个协议用空格分隔 + * @since 5.5.7 + */ + public void setSslProtocols(String sslProtocols) { + this.sslProtocols = sslProtocols; + } + + /** + * 获取指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 + * + * @return 指定实现javax.net.SocketFactory接口的类的名称, 这个类将被用于创建SMTP的套接字 + */ + public String getSocketFactoryClass() { + return socketFactoryClass; + } + + /** + * 设置指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 + * + * @param socketFactoryClass 指定实现javax.net.SocketFactory接口的类的名称,这个类将被用于创建SMTP的套接字 + * @return this + */ + public MailAccount setSocketFactoryClass(String socketFactoryClass) { + this.socketFactoryClass = socketFactoryClass; + return this; + } + + /** + * 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true + * + * @return 如果设置为true, 未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true + */ + public boolean isSocketFactoryFallback() { + return socketFactoryFallback; + } + + /** + * 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true + * + * @param socketFactoryFallback 如果设置为true,未能创建一个套接字使用指定的套接字工厂类将导致使用java.net.Socket创建的套接字类, 默认值为true + * @return this + */ + public MailAccount setSocketFactoryFallback(boolean socketFactoryFallback) { + this.socketFactoryFallback = socketFactoryFallback; + return this; + } + + /** + * 获取指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 + * + * @return 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 + */ + public int getSocketFactoryPort() { + return socketFactoryPort; + } + + /** + * 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 + * + * @param socketFactoryPort 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 + * @return this + */ + public MailAccount setSocketFactoryPort(int socketFactoryPort) { + this.socketFactoryPort = socketFactoryPort; + return this; + } + + /** + * 设置SMTP超时时长,单位毫秒,缺省值不超时 + * + * @param timeout SMTP超时时长,单位毫秒,缺省值不超时 + * @return this + * @since 4.1.17 + */ + public MailAccount setTimeout(long timeout) { + this.timeout = timeout; + return this; + } + + /** + * 设置Socket连接超时值,单位毫秒,缺省值不超时 + * + * @param connectionTimeout Socket连接超时值,单位毫秒,缺省值不超时 + * @return this + * @since 4.1.17 + */ + public MailAccount setConnectionTimeout(long connectionTimeout) { + this.connectionTimeout = connectionTimeout; + return this; + } + + /** + * 设置Socket写出超时值,单位毫秒,缺省值不超时 + * + * @param writeTimeout Socket写出超时值,单位毫秒,缺省值不超时 + * @return this + * @since 5.8.3 + */ + public MailAccount setWriteTimeout(long writeTimeout) { + this.writeTimeout = writeTimeout; + return this; + } + + /** + * 获取自定义属性列表 + * + * @return 自定义参数列表 + * @since 5.6.4 + */ + public Map getCustomProperty() { + return customProperty; + } + + /** + * 设置自定义属性,如mail.smtp.ssl.socketFactory + * + * @param key 属性名,空白被忽略 + * @param value 属性值, null被忽略 + * @return this + * @since 5.6.4 + */ + public MailAccount setCustomProperty(String key, Object value) { + if (StrUtil.isNotBlank(key) && ObjectUtil.isNotNull(value)) { + this.customProperty.put(key, value); + } + return this; + } + + /** + * 获得SMTP相关信息 + * + * @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)); + if (this.timeout > 0) { + p.put(SMTP_TIMEOUT, String.valueOf(this.timeout)); + } + if (this.connectionTimeout > 0) { + p.put(SMTP_CONNECTION_TIMEOUT, String.valueOf(this.connectionTimeout)); + } + // issue#2355 + if (this.writeTimeout > 0) { + p.put(SMTP_WRITE_TIMEOUT, String.valueOf(this.writeTimeout)); + } + + p.put(MAIL_DEBUG, String.valueOf(this.debug)); + + if (this.starttlsEnable) { + //STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + p.put(STARTTLS_ENABLE, "true"); + + if (null == this.sslEnable) { + //为了兼容旧版本,当用户没有此项配置时,按照starttlsEnable开启状态时对待 + this.sslEnable = true; + } + } + + // SSL + if (null != this.sslEnable && this.sslEnable) { + p.put(SSL_ENABLE, "true"); + p.put(SOCKET_FACTORY, socketFactoryClass); + p.put(SOCKET_FACTORY_FALLBACK, String.valueOf(this.socketFactoryFallback)); + p.put(SOCKET_FACTORY_PORT, String.valueOf(this.socketFactoryPort)); + // issue#IZN95@Gitee,在Linux下需自定义SSL协议版本 + if (StrUtil.isNotBlank(this.sslProtocols)) { + p.put(SSL_PROTOCOLS, this.sslProtocols); + } + } + + // 补充自定义属性,允许自定属性覆盖已经设置的值 + p.putAll(this.customProperty); + + return p; + } + + /** + * 如果某些值为null,使用默认值 + * + * @return this + */ + public MailAccount defaultIfEmpty() { + // 去掉发件人的姓名部分 + final String fromAddress = InternalMailUtil.parseFirstAddress(this.from, this.charset).getAddress(); + + if (StrUtil.isBlank(this.host)) { + // 如果SMTP地址为空,默认使用smtp.<发件人邮箱后缀> + this.host = StrUtil.format("smtp.{}", StrUtil.subSuf(fromAddress, fromAddress.indexOf('@') + 1)); + } + if (StrUtil.isBlank(user)) { + // 如果用户名为空,默认为发件人(issue#I4FYVY@Gitee) + //this.user = StrUtil.subPre(fromAddress, fromAddress.indexOf('@')); + this.user = fromAddress; + } + if (null == this.auth) { + // 如果密码非空白,则使用认证模式 + this.auth = (false == StrUtil.isBlank(this.pass)); + } + if (null == this.port) { + // 端口在SSL状态下默认与socketFactoryPort一致,非SSL状态下默认为25 + this.port = (null != this.sslEnable && this.sslEnable) ? this.socketFactoryPort : 25; + } + if (null == this.charset) { + // 默认UTF-8编码 + this.charset = CharsetUtil.CHARSET_UTF_8; + } + + return this; + } + + @Override + public String toString() { + return "MailAccount [host=" + host + ", port=" + port + ", auth=" + auth + ", user=" + user + ", pass=" + (StrUtil.isEmpty(this.pass) ? "" : "******") + ", from=" + from + ", startttlsEnable=" + + starttlsEnable + ", socketFactoryClass=" + socketFactoryClass + ", socketFactoryFallback=" + socketFactoryFallback + ", socketFactoryPort=" + socketFactoryPort + "]"; + } } diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailException.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailException.java index ced09a290..cc199d455 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailException.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailException.java @@ -7,33 +7,34 @@ import java.io.Serial; /** * 邮件异常 + * * @author xiaoleilu */ -public class MailException extends RuntimeException{ - @Serial +public class MailException extends RuntimeException { + @Serial private static final long serialVersionUID = 8247610319171014183L; - public MailException(Throwable e) { - super(ExceptionUtil.getMessage(e), e); - } + public MailException(Throwable e) { + super(ExceptionUtil.getMessage(e), e); + } - public MailException(String message) { - super(message); - } + public MailException(String message) { + super(message); + } - public MailException(String messageTemplate, Object... params) { - super(StrUtil.format(messageTemplate, params)); - } + public MailException(String messageTemplate, Object... params) { + super(StrUtil.format(messageTemplate, params)); + } - public MailException(String message, Throwable throwable) { - super(message, throwable); - } + public MailException(String message, Throwable throwable) { + super(message, throwable); + } - public MailException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) { - super(message, throwable, enableSuppression, writableStackTrace); - } + public MailException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) { + super(message, throwable, enableSuppression, writableStackTrace); + } - public MailException(Throwable throwable, String messageTemplate, Object... params) { - super(StrUtil.format(messageTemplate, params), throwable); - } + public MailException(Throwable throwable, String messageTemplate, Object... params) { + super(StrUtil.format(messageTemplate, params), throwable); + } } diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java index 0400700b4..040cc572a 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java @@ -5,13 +5,12 @@ import cn.hutool.core.io.IoUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.StrUtil; -import org.dromara.common.core.utils.SpringUtils; -import org.dromara.common.core.utils.StringUtils; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - import jakarta.mail.Authenticator; import jakarta.mail.Session; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; import java.io.File; import java.io.InputStream; diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/UserPassAuthenticator.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/UserPassAuthenticator.java index 52ebd782c..fbbe5e371 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/UserPassAuthenticator.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/UserPassAuthenticator.java @@ -11,23 +11,23 @@ import jakarta.mail.PasswordAuthentication; */ public class UserPassAuthenticator extends Authenticator { - private final String user; - private final String pass; + private final String user; + private final String pass; - /** - * 构造 - * - * @param user 用户名 - * @param pass 密码 - */ - public UserPassAuthenticator(String user, String pass) { - this.user = user; - this.pass = pass; - } + /** + * 构造 + * + * @param user 用户名 + * @param pass 密码 + */ + public UserPassAuthenticator(String user, String pass) { + this.user = user; + this.pass = pass; + } - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(this.user, this.pass); - } + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(this.user, this.pass); + } }