极简邮箱注册系统流程说明
项目概述
这是一个基于 Spring Boot 的邮箱验证注册系统,采用经典的三层架构模式,实现用户通过邮箱验证码进行安全注册的功能。
技术栈
- 后端框架: Spring Boot 2.6.13
- 数据库: MySQL 8.0
- ORM 框架: MyBatis-Plus 3.5.3.1
- 缓存: Redis
- 邮件服务: Spring Boot Mail (QQ 邮箱 SMTP)
- 工具库: Lombok、Apache Commons Lang3
项目结构
src/
├── main/
│ ├── java/com/example/registration/
│ │ ├── RegistrationApplication.java # 启动类
│ │ ├── controller/
│ │ │ └── UserController.java # 控制器层
│ │ ├── service/
│ │ │ ├── UserService.java # 用户业务逻辑
│ │ │ └── EmailService.java # 邮件服务
│ │ ├── entity/
│ │ │ ├── User.java # 用户实体
│ │ │ └── UserRegisterDTO.java # 注册数据传输对象
│ │ └── mapper/
│ │ └── UserMapper.java # 数据访问层
│ └── resources/
│ └── application.yml # 应用配置
└── test/ # 测试代码
数据库设计
用户表结构 (user)
CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
username VARCHAR(50) NOT NULL COMMENT '用户名',
email VARCHAR(100) NOT NULL UNIQUE COMMENT '邮箱地址',
password VARCHAR(255) NOT NULL COMMENT '密码',
is_verified BOOLEAN DEFAULT FALSE COMMENT '邮箱验证状态'
);
系统配置
application.yml 配置说明
spring:
datasource:
url: jdbc:mysql://localhost:3306/registration?useSSL=false
username: root
password: your_password_here # ⚠️ 请修改为实际密码
driver-class-name: com.mysql.cj.jdbc.Driver
mail:
host: smtp.qq.com
username: ********** # ⚠️ 请修改为实际邮箱
password: your_auth_code_here # ⚠️ 请修改为邮箱授权码
port: 465
properties:
mail.smtp.auth: true
mail.smtp.starttls.enable: true
mail.smtp.ssl.enable: true
redis:
host: localhost
port: 6379
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
配置说明:
- 数据库配置: MySQL 连接信息,需要创建名为
registration
的数据库 - 邮件配置: QQ 邮箱 SMTP 服务器配置,需要开启邮箱的 SMTP 服务并获取授权码
- Redis 配置: 用于存储验证码的缓存服务
- MyBatis-Plus 配置: Mapper 文件路径配置
核心流程详解
1. 发送验证码流程
接口信息
- 请求路径:
POST /api/user/send-code
- 请求参数:
email
(邮箱地址) - 响应: "验证码已发送"
处理流程
sequenceDiagram
participant Client as 客户端
participant Controller as UserController
participant Service as UserService
participant DB as 数据库
participant Redis as Redis缓存
participant Email as EmailService
participant SMTP as 邮件服务器
Client->>Controller: POST /api/user/send-code
Controller->>Service: sendVerificationCode(email)
Service->>DB: 检查邮箱是否已注册
alt 邮箱已存在
Service-->>Controller: 抛出"邮箱已注册"异常
Controller-->>Client: 返回错误信息
else 邮箱未注册
Service->>Service: 生成6位随机验证码
Service->>Redis: 存储验证码(5分钟有效期)
Service->>Email: sendVerificationCode(email, code)
Email->>SMTP: 发送邮件
Email-->>Service: 发送成功
Service-->>Controller: 完成
Controller-->>Client: "验证码已发送"
end
核心代码逻辑
1. 控制器层代码 (UserController.java)
@PostMapping("/send-code")
public ResponseEntity<String> sendCode(@RequestParam String email) {
userService.sendVerificationCode(email);
return ResponseEntity.ok("验证码已发送");
}
2. 业务逻辑层代码 (UserService.java)
public void sendVerificationCode(String email) {
// 检查邮箱是否已注册
if (userMapper.selectOne(new LambdaQueryWrapper<User>()
.eq(User::getEmail, email)) != null) {
throw new RuntimeException("邮箱已注册");
}
// 生成6位随机验证码
String code = RandomStringUtils.randomNumeric(6);
// 存储到Redis,5分钟有效期
redisTemplate.opsForValue().set(
"email:code:" + email, code, 5, TimeUnit.MINUTES);
// 发送邮件
emailService.sendVerificationCode(email, code);
}
3. 邮件服务代码 (EmailService.java)
public void sendVerificationCode(String toEmail, String code) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(fromEmail);
message.setTo(toEmail);
message.setSubject("邮箱验证码");
message.setText("您的验证码是: " + code + ",5分钟内有效");
mailSender.send(message);
}
流程说明:
- 邮箱重复检查: 使用 MyBatis-Plus 的 LambdaQueryWrapper 查询数据库,确保邮箱未被注册
- 验证码生成: 使用 Apache Commons Lang3 的 RandomStringUtils 生成 6 位数字验证码
- 缓存存储: 将验证码存储到 Redis,键名格式为"email:code:邮箱地址",有效期 5 分钟
- 邮件发送: 通过 EmailService 发送包含验证码的邮件
2. 用户注册流程
接口信息
- 请求路径:
POST /api/user/register
- 请求体: UserRegisterDTO 对象
{ "username": "用户名", "email": "邮箱地址", "password": "密码", "code": "验证码" }
- 响应: "注册成功"
处理流程
sequenceDiagram
participant Client as 客户端
participant Controller as UserController
participant Service as UserService
participant Redis as Redis缓存
participant DB as 数据库
Client->>Controller: POST /api/user/register
Controller->>Controller: 构造User对象
Controller->>Service: register(user, code)
Service->>Redis: 获取存储的验证码
alt 验证码不存在或错误
Service-->>Controller: 抛出"验证码错误"异常
Controller-->>Client: 返回错误信息
else 验证码正确
Service->>DB: 插入用户数据
Service->>Redis: 删除已使用的验证码
Service-->>Controller: "注册成功"
Controller-->>Client: "注册成功"
end
核心代码逻辑
1. 控制器层代码 (UserController.java)
@PostMapping("/register")
public ResponseEntity<String> register(@RequestBody UserRegisterDTO dto) {
User user = new User();
user.setEmail(dto.getEmail());
user.setUsername(dto.getUsername());
user.setPassword(dto.getPassword()); // ⚠️ 实际项目中应该加密密码
userService.register(user, dto.getCode());
return ResponseEntity.ok("注册成功");
}
2. 业务逻辑层代码 (UserService.java)
public String register(User user, String inputCode) {
// 获取Redis中存储的验证码
String storedCode = redisTemplate.opsForValue()
.get("email:code:" + user.getEmail());
// 验证码校验
if (storedCode == null || !storedCode.equals(inputCode)) {
throw new RuntimeException("验证码错误");
}
// 保存用户到数据库
userMapper.insert(user);
// 清除已使用的验证码
redisTemplate.delete("email:code:" + user.getEmail());
return "注册成功";
}
3. 数据传输对象 (UserRegisterDTO.java)
@Data
public class UserRegisterDTO {
private String username;
private String email;
private String password;
private String code; // 验证码
}
4. 用户实体类 (User.java)
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String email;
private String password; // ⚠️ 建议加密存储
@TableField("is_verified")
private Boolean isVerified = false;
}
流程说明:
- DTO 转换: Controller 层将 UserRegisterDTO 转换为 User 实体对象
- 验证码校验: 从 Redis 中获取存储的验证码,与用户输入的验证码进行比较
- 数据持久化: 验证通过后,使用 MyBatis-Plus 的 insert 方法将用户信息保存到数据库
- 缓存清理: 注册成功后清除 Redis 中的验证码
关键技术实现
1. 邮件服务实现
完整的邮件服务类:
@Service
public class EmailService {
@Autowired
private JavaMailSender mailSender;
@Value("${spring.mail.username}")
private String fromEmail;
public void sendVerificationCode(String toEmail, String code) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(fromEmail);
message.setTo(toEmail);
message.setSubject("邮箱验证码");
message.setText("您的验证码是: " + code + ",5分钟内有效");
mailSender.send(message);
}
}
特点:
- 使用 Spring Boot Mail Starter
- 配置 QQ 邮箱 SMTP 服务器
- 支持 SSL 加密传输
- 简单文本邮件格式
2. 缓存机制
Redis 操作示例:
// 存储验证码
redisTemplate.opsForValue().set(
"email:code:" + email, code, 5, TimeUnit.MINUTES);
// 获取验证码
String storedCode = redisTemplate.opsForValue()
.get("email:code:" + email);
// 删除验证码
redisTemplate.delete("email:code:" + email);
特点:
- 使用 Redis 存储临时验证码
- 设置 5 分钟过期时间
- 键名采用统一命名规范
3. 数据访问层
Mapper 接口:
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 继承BaseMapper即拥有基础CRUD方法
}
查询示例:
// 查询邮箱是否存在
User existUser = userMapper.selectOne(
new LambdaQueryWrapper<User>().eq(User::getEmail, email));
// 插入新用户
userMapper.insert(user);
特点:
- 基于 MyBatis-Plus 框架
- 继承 BaseMapper 获得基础 CRUD 功能
- 使用 LambdaQueryWrapper 进行类型安全查询
4. 异常处理
当前实现:
// 业务异常抛出
if (storedCode == null || !storedCode.equals(inputCode)) {
throw new RuntimeException("验证码错误");
}
if (userMapper.selectOne(new LambdaQueryWrapper<User>()
.eq(User::getEmail, email)) != null) {
throw new RuntimeException("邮箱已注册");
}
建议改进 - 全局异常处理器:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> handleRuntimeException(RuntimeException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
特点:
- 统一使用 RuntimeException 抛出业务异常
- 建议在 Controller 层配置全局异常处理器
安全考虑
1. 验证码安全
- 验证码有效期限制(5 分钟)
- 一次性使用机制
- 随机生成,难以预测
2. 邮箱唯一性
- 数据库层面设置邮箱唯一约束
- 注册前进行重复性检查
3. 密码安全
- 建议在实际项目中对密码进行加密存储
- 可以集成 Spring Security 进行密码编码
部署要求
环境依赖
- JDK 1.8+
- MySQL 8.0+
- Redis 6.0+
- Maven 3.6+
数据库初始化
创建数据库和表:
-- 创建数据库
CREATE DATABASE registration CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 使用数据库
USE registration;
-- 创建用户表
CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
username VARCHAR(50) NOT NULL COMMENT '用户名',
email VARCHAR(100) NOT NULL UNIQUE COMMENT '邮箱地址',
password VARCHAR(255) NOT NULL COMMENT '密码',
is_verified BOOLEAN DEFAULT FALSE COMMENT '邮箱验证状态',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
);
配置项
-
数据库连接: 修改 application.yml 中的数据库连接信息
spring: datasource: url: jdbc:mysql://localhost:3306/registration?useSSL=false username: your_db_username # ⚠️ 修改为实际用户名 password: your_db_password # ⚠️ 修改为实际密码
-
邮件服务: 配置邮箱 SMTP 服务器和授权码
spring: mail: username: ********** # ⚠️ 修改为实际邮箱 password: your_auth_code_here # ⚠️ 修改为邮箱授权码
-
Redis 连接: 设置 Redis 服务器地址和端口
spring: redis: host: localhost port: 6379 password: your_redis_password # ⚠️ 如有密码请配置
API 测试示例
1. 发送验证码 API 测试
请求示例:
curl -X POST "http://localhost:8080/api/user/send-code" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "email=test@example.com" # ⚠️ 请使用真实邮箱进行测试
Postman 测试:
- 方法: POST
- URL:
http://localhost:8080/api/user/send-code
- 参数:
email=test@example.com
- 预期响应:
验证码已发送
2. 用户注册 API 测试
请求示例:
curl -X POST "http://localhost:8080/api/user/register" \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"email": "test@example.com",
"password": "123456",
"code": "123456" # 请使用收到的实际验证码
}'
Postman 测试:
- 方法: POST
- URL:
http://localhost:8080/api/user/register
- Body: JSON 格式
{
"username": "testuser",
"email": "test@example.com",
"password": "123456",
"code": "请输入收到的验证码"
}
- 预期响应:
注册成功
3. 常见错误响应
邮箱已注册:
{
"error": "邮箱已注册"
}
验证码错误:
{
"error": "验证码错误"
}
验证码过期:
{
"error": "验证码错误"
}
扩展建议
功能扩展
- 短信验证: 增加手机号验证方式
- 图形验证码: 防止机器人恶意注册
- 密码强度验证: 增加密码复杂度要求
- 用户状态管理: 增加用户激活/禁用功能
技术优化
- 全局异常处理: 统一处理业务异常
- 参数校验: 使用 Bean Validation 进行参数验证
- 日志记录: 增加详细的操作日志
- 接口文档: 集成 Swagger 生成 API 文档
⚠️ 重要提醒:隐私和安全
代码中的隐私信息处理
本文档中所有带有 ⚠️
标记的配置项都需要根据实际情况修改:
- 数据库密码:
your_password_here
→ 请设置安全的数据库密码 - 邮箱地址:
**********
→ 请使用真实的邮箱地址 - 邮箱授权码:
your_auth_code_here
→ 请使用邮箱的授权码(非登录密码) - Redis 密码: 如果 Redis 设置了密码,请配置正确的密码
生产环境安全建议
-
密码加密: 示例代码中密码是明文存储,生产环境必须使用 BCrypt 等加密算法
// 推荐使用 Spring Security 的 BCryptPasswordEncoder @Autowired private BCryptPasswordEncoder passwordEncoder; // 注册时加密密码 user.setPassword(passwordEncoder.encode(dto.getPassword()));
-
配置文件安全:
- 不要将包含敏感信息的配置文件提交到公共代码仓库
- 使用环境变量或配置中心管理敏感配置
- 使用 Spring Profiles 区分不同环境配置
-
验证码安全增强:
- 增加发送频率限制(如:1 分钟内只能发送一次)
- 增加 IP 限制防止恶意攻击
- 考虑添加图形验证码
-
HTTPS: 生产环境必须使用 HTTPS 协议
QQ 邮箱授权码获取步骤
- 登录 QQ 邮箱网页版
- 点击设置 → 账户
- 找到"POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV 服务"
- 开启"IMAP/SMTP 服务"
- 生成授权码(16 位字符)
- 将授权码配置到
application.yml
的spring.mail.password
总结
该邮箱注册系统实现了完整的"发送验证码 → 验证注册"流程,采用了现代化的技术栈和标准的分层架构。系统具有良好的可扩展性和维护性,可以作为其他项目的基础模块进行复用和扩展。
通过 Redis 缓存机制确保了验证码的时效性,通过数据库约束保证了数据的一致性,通过邮件服务实现了用户身份的验证,整个流程设计合理,安全可靠。
注意: 本文档提供的是基础版本的实现,在实际生产环境中请务必加强安全性设计,包括但不限于密码加密、HTTPS 部署、频率限制等安全措施。
#邮箱注册#