Skip to content
三方集成2026年8月7日
# 腾讯云 # Java

SpringBoot + Redis 实现邮箱验证码发送

邮箱验证码几乎是所有系统都需要使用的功能,例如:

  • 用户注册
  • 找回密码
  • 登录验证
  • 修改邮箱
  • 二次身份验证

很多初学者都会犯一个错误:发送验证码后直接把验证码返回给前端。

例如:

json
{
    "code":"123456",
    "msg":"success"
}

这种方式虽然方便测试,但是在真实业务中存在严重安全风险。

正确的做法应该是:

服务端生成验证码 → 发送邮件 → 将验证码缓存到 Redis → 前端仅收到发送成功提示 → 用户提交验证码时由服务端完成校验。

本文将介绍这种生产环境推荐方案。


整体流程

整个发送流程如下:

text
客户端

    │ 请求发送验证码

SpringBoot

    ├── 生成6位验证码

    ├── 写入 Redis(60秒)

    ├── 渲染 HTML 邮件

    └── QQ SMTP发送邮件


      用户邮箱

-------------------------

用户输入验证码




SpringBoot



从 Redis 获取验证码



比较是否一致



成功



继续注册/登录业务

整个过程中,验证码始终不会暴露给前端。


QQ邮箱开启 SMTP 服务

首先需要开启 QQ 邮箱 SMTP 服务。

进入:

设置 → 账户

开启

POP3/SMTP服务

完成验证以后,会获得一串 授权码

例如:

xxxxxxxxxxxxxxxx

注意:

这里不是QQ密码,而是SMTP授权码。


Maven 依赖

项目需要引入邮件发送依赖以及 Freemarker 模板引擎。

xml
<!-- 邮件 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<!-- 模板引擎 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

<!-- Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

邮箱配置

配置 QQ SMTP。

yaml
spring:
  mail:
    host: smtp.qq.com
    port: 587
    username: xxxx@qq.com
    password: xxxxxxxxxxxx
    protocol: smtp

    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true

  data:
    redis:
      host: localhost
      port: 6379

邮件模板

推荐将 HTML 模板放到:

resources/templates/mailtemplate.ftl

模板可以使用 Freemarker 渲染验证码:

html
验证码:

<span style="font-size:30px;color:#3b82f6;">
    ${code}
</span>

五分钟内有效。

相比 MessageFormat,占位符更加直观,也更符合 Freemarker 的使用方式。


Redis 缓存验证码

生成验证码以后,不再返回前端,而是写入 Redis。

例如:

java
String code = RandomUtil.randomNumbers(6);

redisTemplate.opsForValue().set(
    "email:code:" + email,
    code,
    Duration.ofMinutes(5)
);

Redis 中的数据如下:

Key

email:code:123@qq.com

Value

482913

TTL

300s

这样验证码将在五分钟后自动失效,无需手动删除。


邮件发送

邮件发送流程保持不变。

主要步骤:

  1. 创建 MimeMessage
  2. 渲染 HTML 模板
  3. 设置收件人
  4. 设置主题
  5. JavaMailSender 发送邮件

发送成功以后,仅返回:

json
{
    "success": true,
    "message": "验证码发送成功"
}

不会返回验证码。


验证码校验

用户输入验证码后,调用校验接口。

例如:

POST

/email/verify

请求:

json
{
    "email":"123@qq.com",
    "code":"482913"
}

服务端流程:

java
String redisCode =
    redisTemplate.opsForValue()
                 .get("email:code:" + email);

if(redisCode == null){
    return "验证码已过期";
}

if(!redisCode.equals(code)){
    return "验证码错误";
}

redisTemplate.delete("email:code:" + email);

// 执行业务

验证成功后建议立即删除 Redis 中的验证码,防止重复使用。


Redis Key 设计

推荐统一管理验证码 Key。

text
email:code:{email}

例如:

email:code:123@qq.com

如果还有短信验证码,可以继续扩展:

sms:code:13800138000

如果还有登录验证码:

login:code:userId

统一命名更加方便维护。


接口设计

发送验证码:

POST /email/send

参数:

json
{
    "email":"123@qq.com"
}

返回:

json
{
    "success":true,
    "message":"发送成功"
}

校验验证码:

POST /email/verify

参数:

json
{
    "email":"123@qq.com",
    "code":"123456"
}

返回:

json
{
    "success":true
}

生产环境建议

实际项目中,建议进一步完善以下能力:

  • 发送频率限制:限制同一邮箱短时间内重复发送验证码,例如 60 秒内只能发送一次。
  • 验证码有效期:建议控制在 5 分钟以内,过期自动失效。
  • 验证码使用次数:校验成功后立即删除,避免重复使用。
  • 验证码复杂度:建议使用 6 位随机数字或数字+字母组合。
  • 异常处理:邮件发送失败时记录日志,并做好异常提示。
  • 异步发送:可结合消息队列或异步线程发送邮件,避免接口阻塞。

总结

本文采用的是生产环境推荐方案,其核心思路如下:

  • 使用 QQ 邮箱 SMTP 发送验证码邮件;
  • 使用 Freemarker 渲染 HTML 邮件模板;
  • 使用 Redis 缓存验证码并设置过期时间;
  • 服务端负责验证码校验,验证码不会返回给前端;
  • 校验成功后立即删除缓存,保证验证码一次性使用。

相比直接将验证码返回给前端,这种方案更加安全,也更符合实际项目开发规范。

网站运行 ,站长活了