ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/GlobalMailAccount.java
@@ -6,42 +6,41 @@ * 全局邮件帐户,依赖于邮件配置文件{@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; } } ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/InternalMailUtil.java
@@ -13,95 +13,96 @@ /** * 邮件内部工具类 * * @author looly * @since 3.2.3 */ public class InternalMailUtil { /** * 将多个字符串邮件地址转为{@link InternetAddress}列表<br> * 单个字符串地址可以是多个地址合并的字符串 * * @param addrStrs 地址数组 * @param charset 编码(主要用于中文用户名的编码) * @return 地址数组 * @since 4.0.3 */ public static InternetAddress[] parseAddressFromStrs(String[] addrStrs, Charset charset) { final List<InternetAddress> 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}列表<br> * 单个字符串地址可以是多个地址合并的字符串 * * @param addrStrs 地址数组 * @param charset 编码(主要用于中文用户名的编码) * @return 地址数组 * @since 4.0.3 */ public static InternetAddress[] parseAddressFromStrs(String[] addrStrs, Charset charset) { final List<InternetAddress> 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]; } /** * 将一个地址字符串解析为多个地址<br> * 地址间使用" "、","、";"分隔 * * @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); } } } /** * 将一个地址字符串解析为多个地址<br> * 地址间使用" "、","、";"分隔 * * @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; } /** * 编码中文字符<br> * 编码失败返回原字符串 * * @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; } /** * 编码中文字符<br> * 编码失败返回原字符串 * * @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; } } ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/Mail.java
@@ -32,452 +32,452 @@ @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; } /** * 设置正文<br> * 正文可以是普通文本也可以是HTML(默认普通文本),可以通过调用{@link #setHtml(boolean)} 设置是否为HTML * * @param content 正文 * @return this */ public Mail setContent(String content) { this.content = content; return this; } /** * 设置正文<br> * 正文可以是普通文本也可以是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; } /** * 获取默认邮件会话<br> * 如果为全局单例的会话,则全局只允许一个邮件帐号,否则每次发送邮件会新建一个新的会话 * * @return 邮件会话 {@link Session} */ private Session getSession() { final Session session = MailUtils.getSession(this.mailAccount, this.useGlobalSession); /** * 获取默认邮件会话<br> * 如果为全局单例的会话,则全局只允许一个邮件帐号,否则每次发送邮件会新建一个新的会话 * * @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 } ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailAccount.java
@@ -18,642 +18,642 @@ * @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"; 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"; // 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"; // 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"; // 其他 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"}; 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; /** * 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; /** * 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 */ 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; /** * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 */ private boolean starttlsEnable = false; /** * 使用 SSL安全连接 */ private Boolean sslEnable; /** * SSL协议,多个协议用空格分隔 */ private String sslProtocols; /** * 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; /** * 指定实现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; /** * SMTP超时时长,单位毫秒,缺省值不超时 */ private long timeout; /** * Socket连接超时值,单位毫秒,缺省值不超时 */ private long connectionTimeout; /** * Socket写出超时值,单位毫秒,缺省值不超时 */ private long writeTimeout; /** * 自定义的其他属性,此自定义属性会覆盖默认属性 */ private final Map<String, Object> customProperty = new HashMap<>(); /** * 自定义的其他属性,此自定义属性会覆盖默认属性 */ private final Map<String, Object> customProperty = new HashMap<>(); // -------------------------------------------------------------- Constructor start // -------------------------------------------------------------- Constructor start /** * 构造,所有参数需自行定义或保持默认值 */ public MailAccount() { } /** * 构造,所有参数需自行定义或保持默认值 */ public MailAccount() { } /** * 构造 * * @param settingPath 配置文件路径 */ public MailAccount(String settingPath) { this(new Setting(settingPath)); } /** * 构造 * * @param settingPath 配置文件路径 */ public MailAccount(String settingPath) { this(new Setting(settingPath)); } /** * 构造 * * @param setting 配置文件 */ public MailAccount(Setting setting) { setting.toBean(this); } /** * 构造 * * @param setting 配置文件 */ public MailAccount(Setting setting) { setting.toBean(this); } // -------------------------------------------------------------- Constructor end // -------------------------------------------------------------- Constructor end /** * 获得SMTP服务器域名 * * @return SMTP服务器域名 */ public String getHost() { return host; } /** * 获得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服务器域名 * * @param host SMTP服务器域名 * @return this */ public MailAccount setHost(String host) { this.host = host; return this; } /** * 获得SMTP服务端口 * * @return SMTP服务端口 */ public Integer getPort() { return port; } /** * 获得SMTP服务端口 * * @return SMTP服务端口 */ public Integer getPort() { return port; } /** * 设置SMTP服务端口 * * @param port SMTP服务端口 * @return this */ public MailAccount setPort(Integer port) { this.port = port; return this; } /** * 设置SMTP服务端口 * * @param port SMTP服务端口 * @return this */ public MailAccount setPort(Integer port) { this.port = port; return this; } /** * 是否需要用户名密码验证 * * @return 是否需要用户名密码验证 */ public Boolean isAuth() { return auth; } /** * 是否需要用户名密码验证 * * @return 是否需要用户名密码验证 */ public Boolean isAuth() { return auth; } /** * 设置是否需要用户名密码验证 * * @param isAuth 是否需要用户名密码验证 * @return this */ public MailAccount setAuth(boolean isAuth) { this.auth = isAuth; return this; } /** * 设置是否需要用户名密码验证 * * @param isAuth 是否需要用户名密码验证 * @return this */ public MailAccount setAuth(boolean isAuth) { this.auth = isAuth; return this; } /** * 获取用户名 * * @return 用户名 */ public String getUser() { return user; } /** * 获取用户名 * * @return 用户名 */ public String getUser() { return user; } /** * 设置用户名 * * @param user 用户名 * @return this */ public MailAccount setUser(String user) { this.user = user; return this; } /** * 设置用户名 * * @param user 用户名 * @return this */ public MailAccount setUser(String user) { this.user = user; return this; } /** * 获取密码 * * @return 密码 */ public String getPass() { return pass; } /** * 获取密码 * * @return 密码 */ public String getPass() { return pass; } /** * 设置密码 * * @param pass 密码 * @return this */ public MailAccount setPass(String pass) { this.pass = pass; return this; } /** * 设置密码 * * @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标准 * * @return 发送方,遵循RFC-822标准 */ public String getFrom() { return from; } /** * 设置发送方,遵循RFC-822标准<br> * 发件人可以是以下形式: * * <pre> * 1. user@xxx.xx * 2. name <user@xxx.xx> * </pre> * * @param from 发送方,遵循RFC-822标准 * @return this */ public MailAccount setFrom(String from) { this.from = from; return this; } /** * 设置发送方,遵循RFC-822标准<br> * 发件人可以是以下形式: * * <pre> * 1. user@xxx.xx * 2. name <user@xxx.xx> * </pre> * * @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; } /** * 是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 * * @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; } /** * 设置是否打开调试模式,调试模式会显示与邮件服务器通信过程,默认不开启 * * @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; } /** * 获取字符集编码 * * @return 编码,可能为{@code null} */ public Charset getCharset() { return charset; } /** * 设置字符集编码,此选项不会修改全局配置,若修改全局配置,请设置此项为{@code null}并设置: * <pre> * System.setProperty("mail.mime.charset", charset); * </pre> * * @param charset 字符集编码,{@code null} 则表示使用全局设置的默认编码,全局编码为mail.mime.charset系统属性 * @return this */ public MailAccount setCharset(Charset charset) { this.charset = charset; return this; } /** * 设置字符集编码,此选项不会修改全局配置,若修改全局配置,请设置此项为{@code null}并设置: * <pre> * System.setProperty("mail.mime.charset", charset); * </pre> * * @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(国内邮箱附件不支持切分的附件名) * * @return 对于超长参数是否切分为多份 */ public boolean isSplitlongparameters() { return splitlongparameters; } /** * 设置对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名)<br> * 注意此项为全局设置,此项会调用 * <pre> * System.setProperty("mail.mime.splitlongparameters", true) * </pre> * * @param splitlongparameters 对于超长参数是否切分为多份 */ public void setSplitlongparameters(boolean splitlongparameters) { this.splitlongparameters = splitlongparameters; } /** * 设置对于超长参数是否切分为多份,默认为false(国内邮箱附件不支持切分的附件名)<br> * 注意此项为全局设置,此项会调用 * <pre> * System.setProperty("mail.mime.splitlongparameters", true) * </pre> * * @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() { /** * 对于文件名是否使用{@link #charset}编码,默认为 {@code true} * * @return 对于文件名是否使用{@link #charset}编码,默认为 {@code true} * @since 5.7.16 */ public boolean isEncodefilename() { return encodefilename; } return encodefilename; } /** * 设置对于文件名是否使用{@link #charset}编码,此选项不会修改全局配置<br> * 如果此选项设置为{@code false},则是否编码取决于两个系统属性: * <ul> * <li>mail.mime.encodefilename 是否编码附件文件名</li> * <li>mail.mime.charset 编码文件名的编码</li> * </ul> * * @param encodefilename 对于文件名是否使用{@link #charset}编码 * @since 5.7.16 */ public void setEncodefilename(boolean encodefilename) { this.encodefilename = encodefilename; } /** * 设置对于文件名是否使用{@link #charset}编码,此选项不会修改全局配置<br> * 如果此选项设置为{@code false},则是否编码取决于两个系统属性: * <ul> * <li>mail.mime.encodefilename 是否编码附件文件名</li> * <li>mail.mime.charset 编码文件名的编码</li> * </ul> * * @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), 而不是使用一个单独的加密通信端口。 * * @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; } /** * 设置是否使用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安全连接 * * @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安全连接 * * @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协议,多个协议用空格分隔 * * @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; } /** * 设置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的套接字 * * @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; } /** * 设置指定实现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 * * @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; } /** * 如果设置为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; } /** * 获取指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 * * @return 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 */ public int getSocketFactoryPort() { return socketFactoryPort; } /** * 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 * * @param socketFactoryPort 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 * @return this */ public MailAccount setSocketFactoryPort(int socketFactoryPort) { this.socketFactoryPort = socketFactoryPort; return this; } /** * 指定的端口连接到在使用指定的套接字工厂。如果没有设置,将使用默认端口 * * @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; } /** * 设置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 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; } /** * 设置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<String, Object> getCustomProperty() { return customProperty; } /** * 获取自定义属性列表 * * @return 自定义参数列表 * @since 5.6.4 */ public Map<String, Object> 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; } /** * 设置自定义属性,如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)); /** * 获得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)); } 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)); p.put(MAIL_DEBUG, String.valueOf(this.debug)); if (this.starttlsEnable) { //STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 p.put(STARTTLS_ENABLE, "true"); if (this.starttlsEnable) { //STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 p.put(STARTTLS_ENABLE, "true"); if (null == this.sslEnable) { //为了兼容旧版本,当用户没有此项配置时,按照starttlsEnable开启状态时对待 this.sslEnable = 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); } } // 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); // 补充自定义属性,允许自定属性覆盖已经设置的值 p.putAll(this.customProperty); return p; } return p; } /** * 如果某些值为null,使用默认值 * * @return this */ public MailAccount defaultIfEmpty() { // 去掉发件人的姓名部分 final String fromAddress = InternalMailUtil.parseFirstAddress(this.from, this.charset).getAddress(); /** * 如果某些值为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; } 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; } 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 + "]"; } @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 + "]"; } } ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailException.java
@@ -7,33 +7,34 @@ /** * 邮件异常 * * @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); } } ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java
@@ -5,13 +5,12 @@ 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; ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/UserPassAuthenticator.java
@@ -11,23 +11,23 @@ */ 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); } }