Spring Boot Email Integration: Build Verification Code System with Redis & Thymeleaf
This tutorial walks through email protocols, third-party SMTP setup for Feishu and QQ, Spring Boot mail starter configuration, core JavaMailSender/MimeMessage/MimeMessageHelper relationships, and a complete email verification code implementation using Redis for storage and Thymeleaf for HTML templates.
Email Transmission Process
When sending an email from QQ Mail to NetEase Mail, the flow involves five steps:
QQ Mail client uses SMTP to send the email to QQ's mail server.
QQ Mail server receives the email and parses the target domain name.
Since the domain belongs to another provider, QQ Mail server forwards the email via SMTP to NetEase Mail server.
NetEase Mail server recognizes its own domain and stores the envelope.
NetEase Mail client comes online, checks the server, and pulls the email using IMAP or POP3.
Enabling Third-Party Email Services
Feishu Mail
Navigate to the email settings, find "Third-party email client login", select a device, and generate an authorization code, username, and sending server (e.g., smtp.feishu.cn).
QQ Mail
Go to Account Center, enable POP3/IMAP/SMTP services, choose a verification method, and generate an authorization code. Other providers like NetEase follow a similar process.
Spring Boot Email Integration
Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>Configuration (application.yml)
spring:
mail:
host: smtp.feishu.cn
username: huag****@ifree8.com
password: 29F7s********
default-encoding: UTF-8Test Sending
@Test
public void test() throws Exception {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setDefaultEncoding("utf-8");
javaMailSender.setHost("smtp.qq.com");
javaMailSender.setPort(465);
javaMailSender.setUsername("[email protected]");
javaMailSender.setPassword("<your password/authorization code>");
javaMailSender.setProtocol("smtps");
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, false);
helper.setFrom("[email protected]", "springdoc");
helper.setTo("[email protected]");
helper.setSubject("Hello");
helper.setText("Hello <strong> World</strong>!", true);
javaMailSender.send(message);
}Core Class Relationships
JavaMailSender
Implements MailSender interface which defines send(). JavaMailSenderImpl is the concrete implementation.
Acts as the "courier" responsible for actually sending the email.
MimeMessage
Extends Message and implements MimePart.
Represents the "package" — defines email content, recipients, headers.
Key methods: setFrom(Address), setRecipients(), setText().
Low-level API; parameters are already wrapped objects (e.g., Address).
MimeMessageHelper
Helper/wrapper class to simplify setting MimeMessage properties.
Accepts raw types like String for setFrom() instead of Address.
Internally delegates to mimeMessage.setFrom() after validation and parsing.
Especially useful for complex emails (HTML + attachments), reducing boilerplate.
Email Verification Code Implementation
Dependencies
<dependencies>
<dependency> org.springframework.boot:spring-boot-starter-web </dependency>
<dependency> org.springframework.boot:spring-boot-starter-test </dependency>
<dependency> org.springframework.boot:spring-boot-starter-mail </dependency>
<dependency> org.springframework.boot:spring-boot-starter-data-redis </dependency>
<dependency> org.apache.commons:commons-pool2 </dependency>
<dependency> org.redisson:redisson-spring-boot-starter </dependency>
<dependency> cn.hutool:hutool-all </dependency>
<dependency> org.springframework.boot:spring-boot-starter-thymeleaf </dependency>
<dependency> ognl:ognl:3.3.4 </dependency>
<dependency> org.projectlombok:lombok </dependency>
</dependencies>Configuration Properties
spring:
mail:
host: smtp.feishu.cn
username: hua******@ifree8.com
password: 29******
default-encoding: UTF-8
mailFrom: hua******@ifree8.com
mailPersonal: 通用Ai智能体
mailSubject: 邮箱验证码
regExpr: ^(?!.*\.\.)[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$
variable: code
htmlTemplate: MailVerifyTemplate.htmlHTML Template (MailVerifyTemplate.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>邮箱验证码</title>
</head>
<body style="font-family: Arial, Helvetica, sans-serif; background-color: #f6f8fa; padding: 20px;">
<div style="max-width: 600px; margin: auto; background: #ffffff; border-radius: 8px; padding: 30px; box-shadow: 0 2px 6px rgba(0,0,0,0.1);">
<h2 style="text-align: center; color: #333333;">邮箱验证码</h2>
<p style="font-size: 16px; color: #333;">您好,</p>
<p style="font-size: 14px; color: #555;">
您正在进行邮箱验证,本次验证码如下(5分钟内有效):
</p>
<div style="text-align: center; margin: 30px 0;">
<span style="font-size: 28px; font-weight: bold; color: #2d89ef; letter-spacing: 3px;" th:text="${code}">88888</span>
</div>
<p style="font-size: 14px; color: #555;">
请在验证页面输入上方验证码完成验证。为保障账号安全,请勿将验证码告知他人。
</p>
<hr style="margin: 30px 0; border: none; border-top: 1px solid #eee;">
<p style="font-size: 12px; color: #999; text-align: center;">
如果这不是您本人的操作,请忽略此邮件。
</p>
</div>
</body>
</html>Custom JavaMailSender Bean
Because the sender is fixed for verification codes, a JavaMailSender bean is initialized once and injected where needed.
Service Interface
public interface AuthService {
ResponseEntity sendCode2Mail(String targetMail);
ResponseEntity verifyMailCode(String mail, String userCode);
}Service Implementation
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {
@Autowired private MailConfig mailConfig;
@Autowired @Qualifier("defaultJavaMailSender") private JavaMailSender javaMailSender;
@Autowired private RedisBase redisBase;
@Autowired private RedisConstant redisConstant;
@Override
public ResponseEntity<String> sendCode2Mail(String targetMail) {
// 1. Validate email
if (StrUtil.isBlank(targetMail)) {
return ResponseEntity.failBusinessException(FAIL, "邮箱不能为空");
}
if (!ReUtil.isMatch(mailConfig.getRegExpr(), targetMail)) {
return ResponseEntity.failBusinessException(FAIL, "邮箱格式不正确");
}
// 2. Process template
TemplateEngine engine = TemplateUtil.createEngine(
new TemplateConfig("templates", TemplateConfig.ResourceMode.CLASSPATH));
Template template = engine.getTemplate(mailConfig.getHtmlTemplate());
int codeNum = NumberUtil.generateRandomNumber(200000, 999999, 1)[0];
String code = String.valueOf(codeNum);
String htmlContent = template.render(Map.of(mailConfig.getVariable(), code));
// 3. Build email
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = null;
try {
helper = new MimeMessageHelper(message, false);
helper.setFrom(mailConfig.getMailFrom(), mailConfig.getMailPersonal());
helper.setTo(targetMail);
helper.setSubject(mailConfig.getMailSubject());
helper.setText(htmlContent, true);
} catch (Exception e) {
log.error("happen error:", e);
return ResponseEntity.failBusinessException(FAIL, "发生异常,请稍后重试!");
}
// 4. Store code in Redis with SETNX (5 min TTL)
boolean setnxFlag = redisBase.setnx(
redisConstant.getMailVerifyCodeKeyPrefix() + targetMail,
code,
redisConstant.getMailVerifyCodeExpireSeconds());
if (!setnxFlag) {
return ResponseEntity.failBusinessException(FAIL, "redis异常");
}
// 5. Send
javaMailSender.send(message);
// 6. Return success
return ResponseEntity.ok(null, "邮箱发送成功,请注意查收!");
}
@Override
public ResponseEntity verifyMailCode(String mail, String registMailCode) {
String key = redisConstant.getMailVerifyCodeKeyPrefix() + mail;
String systemCode = (String) redisBase.get(key);
if (systemCode == null) {
return ResponseEntity.failBusinessException(FAIL,
"尚未存在邮箱验证码或验证码已过期,请重新发送邮箱验证码!");
}
if (!StrUtil.equals(registMailCode, systemCode)) {
return ResponseEntity.failBusinessException(FAIL, "验证码错误!");
}
return ResponseEntity.ok();
}
}Summary
Email protocol basics: SMTP for sending, IMAP/POP3 for receiving.
End-to-end flow between client and server across providers.
How to enable third-party client access for Feishu and QQ Mail.
Spring Boot integration with spring-boot-starter-mail, configuration, and core classes ( JavaMailSender, MimeMessage, MimeMessageHelper).
Complete verification code system using Redis for atomic SETNX storage with TTL, Thymeleaf for HTML templating, and Hutool utilities for validation and random generation.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
