SpringBoot+QQ邮箱发送邮件

一开始自己做项目的时候,有一个登录和注册的功能,这个功能也是老生常谈了,之前一直是用用户名 + 密码做的。然后突然想用手机号 + 验证码来玩一玩,当时还去阿里云买了个短信服务,结果买是买完了,但是接入短信服务需要进行企业认证,就不了了之了。

既然手机号 + 验证码行不通,那就降级用邮箱号 + 验证码。不过由于个人原因,就只能用 QQ 邮箱了。

配置配置

用 SpringBoot 发送邮件,有官方 Starter:spring-boot-starter-mail,底层是 JavaMailSender。

添加依赖:JDK17 + SpringBoot 3.0.0。

XML 复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>3.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
            <version>3.0.0</version>
        </dependency>
    </dependencies>

添加 yml 配置:

bash 复制代码
server:
  port: 8080

spring:
  application:
    name: email
  mail:
    host: smtp.qq.com
    port: 465
    username: xxx@qq.com  # 你的邮箱地址
    password: xxx  # 授权码,不是登录密码
    protocol: smtp
    default-encoding: UTF-8
    properties:
      mail:
        smtp:
          auth: true
          ssl:
            enable: true
          socketFactory:
            port: 465
            class: javax.net.ssl.SSLSocketFactory

这里的 username 就是要用哪个邮箱发送,比如我有两个邮箱:A@qq.com,B@qq.com,我用 A 邮箱给 B 邮箱发消息,这里的 username 就填 A@qq.com。

password 可不是这个邮箱账号的密码,而是这个邮箱账号的授权码。QQ 邮箱网页 > 设置 > 账号与安全 > 安全设置 > 开启服务,获取授权码。

username + password 是邮件服务器的登录凭证,让 Spring Boot 以这个邮箱的身份发送邮件。

发送文本消息邮件

用邮件发送验证码,就是直接写文本。

java 复制代码
@RestController
public class EmailController {
    @Autowired
    private JavaMailSender javaMailSender;

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

    @PostMapping("/sendText")
    public Integer sendText(String to) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from);
        message.setTo(to);
        // 邮件主题
        message.setSubject("Steam - 登录验证码");
        // 邮件内容
        message.setText("您正在登录steam,验证码是:406406,5分钟内有效!");
        javaMailSender.send(message);
        // TODO:将验证码保存到Redis中,设置ttl=5分钟
        return 200;
    }
}

from 是发送方,to 是接收方,subject 就是邮件主题,text 是邮件内容。

完整的验证码功能,还是把验证码保存起来,可以用数据库也可以用 Redis 或者 ConcurrentHashMap 都行。

现在发送验证码是成功了,但是还有其他类型的邮件:

这种邮件可比纯文本好看多了,这些邮件是怎么发送的呢?

发送HTML邮件

这种邮件看起来这么漂亮,一看就是老HTML了。

那我也写一个HTML页面发送过去。

java 复制代码
    @PostMapping("/sendHtml")
    public Integer sendHtml(String to) throws MessagingException, IOException {
        MimeMessage message = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject("欢迎来到我的第一个页面");
        String str = Files.readString(Paths.get("C:\\Users\\admin\\Downloads\\hello-world.html"));
        // 把html文件读取出来,保存为字符串
        // public void setText(String text, boolean html)
        helper.setText(str, true);
        javaMailSender.send(message);
        return 200;
    }

这个邮件的内容是什么呢?我放在文章的最后了,其实我也是让 AI 写的。

邮件是可以看到图片的,但是点击按钮不会显示 "hello world",因为邮件是禁用 js 的。

为什么邮件要禁用 js 呢?

因为邮件要保证安全,而 js 可以实现很多逻辑,如果邮件可以运行 js,那你点击一个垃圾邮件,这个邮件就可以通过 js 获取你的个人信息,给恶意接口发送请求等等。

所以,邮件里面的交互是很少的,更多的是放一个超链接跳转浏览器页面。

不仅如此,邮件也不支持外部 css,像是在 html 里面通过 <link> 标签引入 css 是不允许的。也就是说,一个邮件就是一个 html 文件,在这个 html 文件里通过 <style> 和行内方式添加样式。

而且,邮件不支持很多现代的 css 样式,甚至 html 推荐用 table、tr、td 来建立结构。如果你在邮件中看到了视频,一般是 gif,such as this:

不过至少 QQ 邮箱是支持外部链接加载图片的。

但是用 html 写邮件就折磨人了:HTML 邮件兼容问题与解决方案 - fecoder - SegmentFault 思否。

发送附件文件

最近也是秋招季节,有些公司是要你上传个人简历,发送邮件给 HR 的。

java 复制代码
    @PostMapping("/sendFile")
    public Integer sendWithAttachment(String to) throws MessagingException {
        MimeMessage message = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject("个人简历");
        helper.setText("HR您好,这是我的个人简历,请你查阅", false);
        File file = new File("C:\\Users\\admin\\Downloads\\简历.pdf");
        helper.addAttachment(file.getName(), file);
        javaMailSender.send(message);
        return 200;
    }

代码

EmailController.java

java 复制代码
@RestController
public class EmailController {
    @Autowired
    private JavaMailSender javaMailSender;

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

    @PostMapping("/sendText")
    public Integer sendText(String to) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from);
        message.setTo(to);
        message.setSubject("Steam - 登录验证码");
        message.setText("您正在登录steam,验证码是:406406,5分钟内有效!");
        javaMailSender.send(message);
        return 200;
    }

    @PostMapping("/sendHtml")
    public Integer sendHtml(String to) throws MessagingException, IOException {
        MimeMessage message = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject("欢迎来到我的第一个页面");
        String str = Files.readString(Paths.get("C:\\Users\\admin\\Downloads\\hello-world.html"));
        // 把html文件读取出来,保存为字符串
        // public void setText(String text, boolean html)
        helper.setText(str, true);
        javaMailSender.send(message);
        return 200;
    }

    @PostMapping("/sendFile")
    public Integer sendWithAttachment(String to) throws MessagingException {
        MimeMessage message = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject("个人简历");
        helper.setText("HR您好,这是我的个人简历,请你查阅", false);
        File file = new File("C:\\Users\\admin\\Downloads\\简历.pdf");
        helper.addAttachment(file.getName(), file);
        javaMailSender.send(message);
        return 200;
    }
}

hello-world.html

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World</title>
<style>
  * { box-sizing: border-box; }
  body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
    background: linear-gradient(135deg, #eef2ff 0%, #f8fafc 100%);
  }
  .card {
    background: #fff;
    padding: 40px 48px;
    border-radius: 20px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, .10);
    text-align: center;
    width: min(90vw, 420px);
  }
  /* Logo 位置:使用指定链接作为背景图片 */
  .logo {
    width: 126px;
    height: 126px;
    margin: 0 auto 24px;
    border-radius: 50%;
    background-image: url("https://cdn-bot.hunyuan.tencent.com/m/yb-chat/logo_animated_126.png");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    box-shadow: 0 6px 16px rgba(99, 102, 241, .25);
  }
  .btn {
    padding: 12px 28px;
    font-size: 16px;
    color: #fff;
    background: #4f46e5;
    border: none;
    border-radius: 999px;
    cursor: pointer;
    transition: transform .15s ease, background .2s ease;
  }
  .btn:hover { background: #4338ca; transform: translateY(-1px); }
  .btn:active { transform: translateY(0); }
  .message {
    margin-top: 24px;
    font-size: 28px;
    font-weight: 600;
    color: #111827;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity .35s ease, transform .35s ease;
  }
  .message.show { opacity: 1; transform: translateY(0); }
</style>
</head>
<body>
  <div class="card">
    <div class="logo" role="img" aria-label="Logo"></div>
    <button class="btn" id="sayBtn">点我一下</button>
    <div class="message" id="msg">Hello World</div>
  </div>

<script>
  const btn = document.getElementById('sayBtn');
  const msg = document.getElementById('msg');
  btn.addEventListener('click', () => {
    msg.classList.add('show');
  });
</script>
</body>
</html>
相关推荐
头发还在的女程序员2 小时前
Java在线租房系统源码解析:Spring Boot+Vue 3+UniApp房源管理与预约看房方案
java·vue.js·spring boot·租房小程序·租房系统·租房软件
FYKJ_20102 小时前
springboot同人周边订单管理系统48955-计算机毕业设计、课程设计
java·javascript·vue.js·spring boot·django·php·课程设计
yuyuxun119 小时前
协同过滤的校园二手物品智能推荐系统的设计与实现 毕业设计-附源码40082
javascript·vue.js·spring boot·python·数据分析·django·课程设计
LGL6030A1 天前
Java学习历程31——初识Spring Boot
java·spring boot·学习
阿达和自己1 天前
springboot学习-springboot引入MyBatis-Plus
spring boot·学习·mybatis
Jackzhang2 天前
[运维排障实录] 第一次遇到 502 别急着重启:按「一条线」把问题查清楚
spring boot
学长毕设2 天前
基于SpringBoot的景点管理系统-附源码
java·spring boot·后端
知守观2 天前
一个方法两个触发口:反编译 XXL-Job 后,我看清了 inner/job 子包的裸奔现状
java·spring boot·微服务
瑞雪兆丰年兮2 天前
JavaWeb-day14-SpringBootWeb原理篇
java·开发语言·spring boot·spring·bean·mybatis·javaweb