Spring Boot整合JavaMail实现邮件发送

news2025/1/30 13:08:16

一. 发送邮件原理

发件人【设置授权码】 - SMTP协议【Simple Mail TransferProtocol - 是一种提供可靠且有效的电子邮件传输的协议】 - 收件人

二. 获取授权码

开通POP3/SMTP,获取授权码

授权码是QQ邮箱推出的,用于登录第三方客户端的专用密码。适用于登录以下服务:POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务。

温馨提醒:为了你的帐户安全,更改QQ密码以及独立密码会触发授权码过期,需要重新获取新的授权码登录

第一步:进入邮箱设置 -> 账户 -> 生成授权码 :【注意】POP3/SMTP ,IMAP/SMTP服务都需要开启

三. SpringBoot发送邮件步骤

1、导入依赖
<!--对邮件的支持jar-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

2、配置邮箱
spring:
  servlet:
    multipart:
      #设置单个文件大小
      max-file-size: 10MB
      #设置单次请求文件的总大小
      max-request-size: 50MB
  mail:
    host: smtp.qq.com   # 设置邮箱主机(服务商),这里使用QQ邮件服务器
    username:   # 设置用户名 - 发送方
    password:    # 设置密码,该处的密码是QQ邮箱开启SMTP的授权码而非QQ密码
    properties:
      mail:
        smtp:
          auth: true  # 必须进行授权认证,它的目的就是阻止他人任意乱发邮件
          starttls: #SMTP加密方式:连接到一个TLS保护连接
            enable: true
            required: true

3、发送邮件的时候可以自定义发送的内容,创建工具类
@Component
@Slf4j
public class MailUtil {

    /**
     * 1. 来源人名
     * 2. 来源内容
     */
    public static final String originalText = "<hr style=\"border: 1px dashed #ef859d2e;margin: 20px 0\">\n" +
            "            <div>\n" +
            "                <div style=\"font-size: 18px;font-weight: bold;color: #C5343E\">\n" +
            "                    %s\n" +
            "                </div>\n" +
            "                <div style=\"margin-top: 6px;font-size: 16px;color: #000000\">\n" +
            "                    <p>\n" +
            "                        %s\n" +
            "                    </p>\n" +
            "                </div>\n" +
            "            </div>";

    /**
     * 发件人
     */
    public static final String replyMail = "你之前的评论收到来自 %s 的回复";
    public static final String commentMail = "你的文章 %s 收到来自 %s 的评论";
    public static final String messageMail = "你收到来自 %s 的留言";
    public static final String loveMail = "你收到来自 %s 的祝福";
    public static final String imMail = "你收到来自 %s 的消息";
    public static final String notificationMail = "你收到来自 %s 的订阅";

    @Autowired
    private JavaMailSender mailSender;

    @Value("${spring.mail.username}")
    private String sendMailer;

    /**
     * 1. 网站名称
     * 2. 邮件类型
     * 3. 发件人
     * 4. 发件内容
     * 5. originalText
     * 6. 网站名称
     */
    private String mailText;

    @PostConstruct
    public void init() {
        this.mailText = "<div style=\"font-family: serif;line-height: 22px;padding: 30px\">\n" +
                "    <div style=\"display: flex;justify-content: center;width: 100%%;max-width: 900px;background-size: cover;border-radius: 10px\"></div>\n" +
                "    <div style=\"margin-top: 20px;display: flex;flex-direction: column;align-items: center\">\n" +
                "        <div style=\"margin: 10px auto 20px;text-align: center\">\n" +
                "            <div style=\"line-height: 32px;font-size: 26px;font-weight: bold;color: #000000\">\n" +
                "                嘿!你在 %s 中收到一条新消息。\n" +
                "            </div>\n" +
                "            <div style=\"font-size: 16px;font-weight: bold;color: rgba(0, 0, 0, 0.19);margin-top: 21px\">\n" +
                "                %s\n" +
                "            </div>\n" +
                "        </div>\n" +
                "        <div style=\"min-width: 250px;max-width: 800px;min-height: 128px;background: #F7F7F7;border-radius: 10px;padding: 32px\">\n" +
                "            <div>\n" +
                "                <div style=\"font-size: 18px;font-weight: bold;color: #C5343E\">\n" +
                "                    %s\n" +
                "                </div>\n" +
                "                <div style=\"margin-top: 6px;font-size: 16px;color: #000000\">\n" +
                "                    <p>\n" +
                "                        %s\n" +
                "                    </p>\n" +
                "                </div>\n" +
                "            </div>\n" +
                "            %s\n" +
                "            <a style=\"width: 150px;height: 38px;background: #ef859d38;border-radius: 32px;display: flex;align-items: center;justify-content: center;text-decoration: none;margin: 40px auto 0\"\n" +
                "               href=\"https://poetize.cn\" target=\"_blank\">\n" +
                "                <span style=\"color: #DB214B\">有朋自远方来</span>\n" +
                "            </a>\n" +
                "        </div>\n" +
                "        <div style=\"margin-top: 20px;font-size: 12px;color: #00000045\">\n" +
                "            此邮件由 %s 自动发出,直接回复无效(一天最多发送 " + CommonConst.COMMENT_IM_MAIL_COUNT + " 条通知邮件和 " + CommonConst.CODE_MAIL_COUNT + " 条验证码邮件),退订请联系站长。\n" +
                "        </div>\n" +
                "    </div>\n" +
                "</div>";
    }

    public String getMailText() {
        return mailText;
    }
}

4、实现类。一般来说验证码可以应用在注册、登录、找回密码等功能上。前端需要传递flag标志,后端进行判断发送哪种邮件

public Result getCodeForForgetPassword(String place, Integer flag) {
        int i = new Random().nextInt(900000) + 100000;
        if (flag == 1) {
           //  任务需求
        } else if (flag == 2) {
            log.info(place + "---" + "邮箱验证码---" + i);

            List<String> mail = new ArrayList<>();
            mail.add(place);
            String text = getCodeMail(i);
            WebInfo webInfo = (WebInfo) UtopianCache.get(CommonConst.WEB_INFO);

            AtomicInteger count = (AtomicInteger) UtopianCache.get(CommonConst.CODE_MAIL + mail.get(0));
            if (count == null || count.get() < CommonConst.CODE_MAIL_COUNT) {
                mailUtil.sendMailMessage(mail, "您有一封来自" + (webInfo == null ? "Utopian" : webInfo.getWebName()) + "的回信!", text);
                if (count == null) {
                    UtopianCache.put(CommonConst.CODE_MAIL + mail.get(0), new AtomicInteger(1), CommonConst.CODE_EXPIRE);
                } else {
                    count.incrementAndGet();
                }
            } else {
                return Result.fail("验证码发送次数过多,请明天再试!");
            }
        }
        UtopianCache.put(CommonConst.FORGET_PASSWORD + place + "_" + flag, Integer.valueOf(i), 300);
        return Result.success();
    }

首先根据标志为flag参数,这里2为注册时候使用的格式。调用getCodeMail方法获取模板内容

private String getCodeMail(int i) {
        WebInfo webInfo = (WebInfo) UtopianCache.get(CommonConst.WEB_INFO);
        String webName = (webInfo == null ? "Utopian" : webInfo.getWebName());
        return String.format(mailUtil.getMailText(),
                webName,
                String.format(MailUtil.imMail, PoetryUtil.getAdminUser().getUsername()),
                UtopianUtil.getAdminUser().getUsername(),
                String.format(codeFormat, i),
                "",
                webName);
    }

模板内容为:【utopian.cn】%s为本次验证的验证码,请在5分钟内完成验证。为保证账号安全,请勿泄漏此验证码。

对用户点击发送的验证码的次数也需要进行统计,不超过三次,否则进行提示

4、发送邮件

这里采用异步的方式,分别填充邮件主题,邮件内容,发送时间等信息

 @Async
    public void sendMailMessage(List<String> to, String subject, String text) {
        log.info("发送邮件===================");
        log.info("to:{}", JSON.toJSONString(to));
        log.info("subject:{}", subject);
        log.info("text:{}", text);
        try {
            //true代表支持复杂的类型
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mailSender.createMimeMessage(), true);
            //邮件发信人
            mimeMessageHelper.setFrom(sendMailer);
            //邮件收信人1或多个
            mimeMessageHelper.setTo(to.toArray(new String[0]));
            //邮件主题
            mimeMessageHelper.setSubject(subject);
            //邮件内容
            mimeMessageHelper.setText(text, true);
            //邮件发送时间
            mimeMessageHelper.setSentDate(new Date());

            //发送邮件
            mailSender.send(mimeMessageHelper.getMimeMessage());

            log.info("发送成功==================");
        } catch (MessagingException e) {
            log.info("发送失败==================");
            log.error(e.getMessage());
        }
    }

image-20231228122738660

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2284066.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

编辑器Vim基本模式和指令 --【Linux基础开发工具】

文章目录 一、编辑器Vim 键盘布局二、Linux编辑器-vim使用三、vim的基本概念正常/普通/命令模式(Normal mode)插入模式(Insert mode)末行模式(last line mode) 四、vim的基本操作五、vim正常模式命令集插入模式从插入模式切换为命令模式移动光标删除文字复制替换撤销上一次操作…

K8S极简教程(4小时快速学会)

1. K8S 概览 1.1 K8S 是什么 K8S官网文档&#xff1a;https://kubernetes.io/zh/docs/home/ 1.2 K8S核心特性 服务发现与负载均衡&#xff1a;无需修改你的应用程序即可使用陌生的服务发现机制。存储编排&#xff1a;自动挂载所选存储系统&#xff0c;包括本地存储。Secret和…

淘宝商品数据解析的应用场景有哪些?

淘宝商品数据解析在多个领域有着广泛的应用场景&#xff0c;以下为你详细介绍&#xff1a; 电商运营与营销 选品分析&#xff1a;通过解析淘宝商品数据&#xff0c;卖家可以了解不同商品的销售情况、价格区间、市场需求热度等信息。例如分析某类商品在不同季节的销量变化&#…

基于OpenCV实现的答题卡自动判卷系统

一、图像预处理 🌄 二、查找答题卡轮廓 📏 三、透视变换 🔄 四、判卷与评分 🎯 五、主函数 六、完整代码+测试图像集 总结 🌟 在这篇博客中,我将分享如何使用Python结合OpenCV库开发一个答题卡自动判卷系统。这个系统能够自动从扫描的答题卡中提取信…

计网week1+2

计网 一.概念 1.什么是Internet 节点&#xff1a;主机及其运行的应用程序、路由器、交换机 边&#xff1a;通信链路&#xff0c;接入网链路主机连接到互联网的链路&#xff0c;光纤、网输电缆 协议&#xff1a;对等层的实体之间通信要遵守的标准&#xff0c;规定了语法、语义…

如何使用tushare pro获取股票数据——附爬虫代码以及tushare积分获取方式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据 总结 一、Tushare 介绍 Tushare 是一个提供中国股市数据的API接口服务&#xff0c;它允许用户…

vim的多文件操作

[rootxxx ~]# vim aa.txt bb.txt cc.txt #多文件操作 next #下一个文件 prev #上一个文件 first #第一个文件 last #最后一个文件 快捷键: ctrlshift^ #当前和上个之间切换 说明&#xff1a;快捷键ctrlshift^&#xff0c…

Mac m1,m2,m3芯片使用nvm安装node14报错

使用nvm安装了node 12/16/18都没有问题&#xff0c;到14就报错了。第一次看到这个报错有点懵&#xff0c;查询资料发现是Mac芯片的问题。 Issue上提供了两个方案&#xff1a; 1、为了在arm64的Mac上安装node 14&#xff0c;需要使用Rosseta&#xff0c;可以通过以下命令安装 …

【云安全】云原生-Docker(五)容器逃逸之漏洞利用

漏洞利用逃逸 通过漏洞利用实现逃逸&#xff0c;主要分为以下两种方式&#xff1a; 1、操作系统层面的内核漏洞 这是利用宿主机操作系统内核中的安全漏洞&#xff0c;直接突破容器的隔离机制&#xff0c;获得宿主机的权限。 攻击原理&#xff1a;容器本质上是通过 Linux 的…

JAVA设计模式:依赖倒转原则(DIP)在Spring框架中的实践体现

文章目录 一、DIP原则深度解析1.1 核心定义1.2 现实比喻 二、Spring中的DIP实现机制2.1 传统实现 vs Spring实现对比 三、Spring中DIP的完整示例3.1 领域模型定义3.2 具体实现3.3 高层业务类3.4 配置类 四、Spring实现DIP的关键技术4.1 依赖注入方式对比4.2 自动装配注解 五、D…

基于微信小程序的健身管理系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

【机器学习】深入探索SVM:支持向量机的原理与应用

目录 &#x1f354; SVM引入 1.1什么是SVM? 1.2支持向量机分类 1.3 线性可分、线性和非线性的区分 &#x1f354; 小结 学习目标 知道SVM的概念 &#x1f354; SVM引入 1.1什么是SVM? 看一个故事&#xff0c;故事是这样子的&#xff1a; 在很久以前的情人节&#xf…

输入带空格的字符串,求单词个数

输入带空格的字符串&#xff0c;求单词个数 __ueooe_eui_sjje__ ---->3syue__jdjd____die_ ---->3shuue__dju__kk ---->3 #include <stdio.h> #include <string.h>// 自定义函数来判断字符是否为空白字符 int isSpace(char c) {return c || c \t || …

[STM32 - 野火] - - - 固件库学习笔记 - - -十二.基本定时器

一、定时器简介 STM32 中的定时器&#xff08;TIM&#xff0c;Timer&#xff09;是其最重要的外设之一&#xff0c;广泛用于时间管理、事件计数和控制等应用。 1.1 基本功能 定时功能&#xff1a;TIM定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中…

kaggle比赛入门 - House Prices - Advanced Regression Techniques(第二部分)

本文承接上一篇 1. 分析住宅类型&#xff08;BldgType&#xff09;的分布以及它们与销售价格&#xff08;SalePrice&#xff09;的关系 # 1. distribution of dwelling types and their relation to sale prices # BldgType: Type of dwellingdwelling_types df[BldgType].v…

数字图像处理:实验六

uu们&#xff01;大家好&#xff0c;2025年的新年就要到来&#xff0c;咸鱼哥在这里祝大家在2025年每天开心快乐&#xff0c;天天挣大钱&#xff0c;自由自在&#xff0c;健健康康&#xff0c;万事如意&#xff01;&#xff08;要是咸鱼哥嘴笨的话&#xff0c;还望大家多多包涵…

C++——list的了解和使用

目录 引言 forward_list与list 标准库中的list 一、list的常用接口 1.list的迭代器 2.list的初始化 3.list的容量操作 4.list的访问操作 5.list的修改操作 6.list的其他操作 二、list与vector的对比 结束语 引言 本篇博客要介绍的是STL中的list。 求点赞收藏评论…

移动光猫怎么自己改桥接模式?

环境&#xff1a; 型号H3-8s 问题描述&#xff1a; 家里宽带用的是H3-8s 光猫&#xff0c;想改桥接模式。 解决方案&#xff1a; 1.默认管理员账号和密码&#xff1a; 账号&#xff1a;CMCCAdmin 密码&#xff1a;aDm8H%MdAWEB页面我试了登陆不了&#xff0c;显示错误 …

MiniMax-01中Lightning Attention的由来(线性注意力进化史)

目录 引言原始注意力线性注意力因果模型存在的问题累加求和操作的限制Lightning AttentionLightning Attention-1Lightning Attention-2 备注 引言 MiniMax-01: Scaling Foundation Models with Lightning Attention表明自己是第一个将线性注意力应用到如此大规模的模型&#…

汽车网络信息安全-ISO/SAE 21434解析(中)

目录 第七章-分布式网络安全活动 1. 供应商能力评估 2. 报价 3. 网络安全职责界定 第八章-持续的网络安全活动 1. 网路安全监控 2. 网络安全事件评估 3. 漏洞分析 4. 漏洞管理 第九章-概念阶段 1. 对象定义 2. 网路安全目标 3. 网络安全概念 第十章 - 产品开发 第十…