RuoYi-Vue-Plus 阅读笔记 – 9 – Mail api 邮件服务
本文最后更新于 225 天前,如有失效请评论区留言。

java 中也提供了 mai 相关的功能,功能相对较弱,现在也停止了更新,目前 java 应用通常使用第三方库 Jakarta Mail 来实现邮件功能

配置

mail:  
  enabled: false  
  host: smtp.163.com  
  port: 465  
  # 是否需要用户名密码验证  
  auth: true  
  # 发送方,遵循RFC-822标准  
  from: xxx@163.com  
  # 用户名(注意:如果使用foxmail邮箱,此处user为qq号)  
  user: xxx@163.com  
  # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)  
  pass: xxxxxxxxxx  
  # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。  
  starttlsEnable: true  
  # 使用SSL安全连接  
  sslEnable: true  
  # SMTP超时时长,单位毫秒,缺省值不超时  
  timeout: 0  
  # Socket连接超时值,单位毫秒,缺省值不超时  
  connectionTimeout: 0

配置类 MailConfig

项目启动时,初始化默认配置信息

@AutoConfiguration  
@EnableConfigurationProperties(MailProperties.class)  
public class MailConfig {  

    @Bean  
    @ConditionalOnProperty(value = "mail.enabled", havingValue = "true")  
    public MailAccount mailAccount(MailProperties mailProperties) {  
        MailAccount account = new MailAccount();  
        account.setHost(mailProperties.getHost());  
        account.setPort(mailProperties.getPort());  
        account.setAuth(mailProperties.getAuth());  
        account.setFrom(mailProperties.getFrom());  
        account.setUser(mailProperties.getUser());  
        account.setPass(mailProperties.getPass());  
        account.setSocketFactoryPort(mailProperties.getPort());  
        account.setStarttlsEnable(mailProperties.getStarttlsEnable());  
        account.setSslEnable(mailProperties.getSslEnable());  
        account.setTimeout(mailProperties.getTimeout());  
        account.setConnectionTimeout(mailProperties.getConnectionTimeout());  
        return account;  
    }  

}

邮件工具类

邮件客户端定义

Mail.java

public class Mail implements Builder<MimeMessage> {  
    @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;  

    /**  
     * 创建邮件客户端  
     *  
     * @param mailAccount 邮件帐号  
     * @return Mail  
     */    
    public static Mail create(MailAccount mailAccount) {  
        return new Mail(mailAccount);  
    }

    /**
     * 构造,使用全局邮件帐户
     */
    public Mail() {
        this(GlobalMailAccount.INSTANCE.getAccount());
    }

    /**
     * 构造
     *
     * @param mailAccount 邮件帐户,如果为null使用默认配置文件的全局邮件配置
     */
    public Mail(MailAccount mailAccount) {
        mailAccount = (null != mailAccount) ? mailAccount : GlobalMailAccount.INSTANCE.getAccount();
        this.mailAccount = mailAccount.defaultIfEmpty();
    }
}

这里的在创建 mail 对象的时候,也提供了创建账户的选择,即,如果没有使用配置类的方式初始化账户信息,通过 GlobalMailAccount 类的功能来创建

public enum GlobalMailAccount {
    INSTANCE;

    private final MailAccount mailAccount;

    /**
     * 构造
     */
    GlobalMailAccount() {
        mailAccount = createDefaultAccount();
    }

    /**
     * 获得邮件帐户
     *
     * @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;
    }
}
public static final String[] MAIL_SETTING_PATHS = new String[]{"config/mail.setting", "config/mailAccount.setting", "mail.setting"};

这里使用了枚举类,INSTANCE 实例对象是唯一的,这里可以通过使用指定 .setting 配置进行邮箱账户的初始化

邮件发送

MailUtils.java

/**
 * 发送邮件给多人
 *
 * @param mailAccount      邮件帐户信息
 * @param useGlobalSession 是否全局共享Session
 * @param tos              收件人列表
 * @param ccs              抄送人列表,可以为null或空
 * @param bccs             密送人列表,可以为null或空
 * @param subject          标题
 * @param content          正文
 * @param imageMap         图片与占位符,占位符格式为cid:${cid}
 * @param isHtml           是否为HTML格式
 * @param files            附件列表
 * @return message-id
 * @since 4.6.3
 */
private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content,
                           Map<String, InputStream> imageMap, boolean isHtml, File... files) {
    final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession);

    // 可选抄送人
    if (CollUtil.isNotEmpty(ccs)) {
        mail.setCcs(ccs.toArray(new String[0]));
    }
    // 可选密送人
    if (CollUtil.isNotEmpty(bccs)) {
        mail.setBccs(bccs.toArray(new String[0]));
    }

    mail.setTos(tos.toArray(new String[0]));
    mail.setTitle(subject);
    mail.setContent(content);
    mail.setHtml(isHtml);
    mail.setFiles(files);

    // 图片
    if (MapUtil.isNotEmpty(imageMap)) {
        for (Map.Entry<String, InputStream> entry : imageMap.entrySet()) {
            mail.addImage(entry.getKey(), entry.getValue());
            // 关闭流
            IoUtil.close(entry.getValue());
        }
    }

    return mail.send();
}

/**  
 * 构建消息  
 *  
 * @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 message-id  
 * @throws MessagingException 发送异常  
 */  
private String doSend() throws MessagingException {  
    final MimeMessage mimeMessage = buildMsg();  
    Transport.send(mimeMessage);  
    return mimeMessage.getMessageID();  
}

doSend 方法使用了 jakarta 工具中的方法,先构建 MimeMessage 类型的邮件信息,再调用 Transport 的 send 执行邮件的发送

使用

用户使用邮箱注册的时候发送验证码,生成随机验证码,将验证码存储在 redis ,并设置有效期,最后发送邮件

/**  
 * 邮箱验证码  
 *  
 * @param email 邮箱  
 */  
@RateLimiter(key = "#email", time = 60, count = 1)  
@GetMapping("/resource/email/code")  
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {  
    if (!mailProperties.getEnabled()) {  
        return R.fail("当前系统没有开启邮箱功能!");  
    }  
    String key = GlobalConstants.CAPTCHA_CODE_KEY + email;  
    String code = RandomUtil.randomNumbers(4);  
    RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));  
    try {  
        MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");  
    } catch (Exception e) {  
        log.error("验证码短信发送异常 => {}", e.getMessage());  
        return R.fail(e.getMessage());  
    }  
    return R.ok();  
}
版权声明:除特殊说明,博客文章均为Gavin原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇