在 Node.js 后端实现邮箱验证码功能
简介
邮箱验证码是现代 Web 应用中常见的功能,用于用户注册、密码重置或身份验证。本文将详细介绍如何在基于 Koa 的 Node.js 应用中实现完整的邮箱验证码功能,包括验证码生成、发送和校验全过程。
技术栈
后端框架:Koa 2.16.1
邮件发送:Nodemailer 6.10.1
开发语言:TypeScript 5.8.3
项目结构:分层架构(控制器、服务层、基础设施层)
项目结构
实现邮箱验证码功能涉及四个主要部分:
基础设施层:
infrastructure/email
- 邮件服务配置和实现工具类:
utils/email-code-verification.util.ts
- 邮箱验证码生成和管理服务层:
services/email-verification.service.ts
- 邮箱验证码业务逻辑控制器层:
controllers/email-verification.controller.ts
- API 接口处理
核心实现
1. 邮箱验证码控制器
控制器负责处理 HTTP 请求,验证参数并调用服务层方法:
// email-verification.controller.ts
export class EmailVerificationController {
// 发送邮箱验证码
public async sendEmailVerificationCode(ctx: Context): Promise<void> {
try {
const { email } = ctx.request.body as EmailRequest;
// 验证邮箱格式
if (!email || !this.validateEmail(email)) {
ctx.status = 400;
ctx.body = error("邮箱地址格式不正确", 400);
return;
}
// 发送验证码
const result = await emailVerificationService.sendVerificationCode(email);
if (result) {
ctx.body = success(null, "验证码已发送,请检查邮箱");
} else {
ctx.status = 500;
ctx.body = error("验证码发送失败,请稍后重试", 500);
}
} catch (err) {
console.error("发送验证码接口错误:", err);
ctx.status = 500;
ctx.body = error("服务器错误", 500);
}
}
// 验证邮箱验证码
public async verifyEmailCode(ctx: Context): Promise<void> {
// 类似的验证和处理逻辑...
}
// 邮箱格式验证
private validateEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
}
2. 邮箱验证码服务
服务层实现业务逻辑,包括生成验证码、保存及校验:
export class EmailVerificationService implements IEmailVerificationService {
// 生成并发送验证码
public async sendVerificationCode(email: string): Promise<boolean> {
try {
// 生成验证码
const code = EmailCodeUtil.generateEmailCode();
// 保存验证码
EmailCodeUtil.saveCode(email, code);
// 发送验证码邮件
const result = await emailService.sendVerificationCode(email, code);
// 发送失败则删除保存的验证码
if (!result) {
EmailCodeUtil.removeCode(email);
}
return result;
} catch (error) {
console.error("发送验证码失败:", error);
return false;
}
}
// 验证邮箱验证码
public verifyCode(email: string, code: string): boolean {
return EmailCodeUtil.verifyCode(email, code);
}
}
3. 验证码工具类
邮箱验证码工具类实现验证码的生成、存储和验证:
// email-code-verification.util.ts
export class EmailCodeUtil {
private static codeStore: Map<string, { code: string; expires: number }> =
new Map();
private static attemptCounters: Map<string, number> = new Map();
/**
* 获取验证码过期时间(毫秒)
*/
protected static getExpireTime(): number {
return config.email.verification.codeExpireMinutes * 60 * 1000;
}
/**
* 生成指定长度的验证码
*/
public static generateCode(length: number = 6): string {
const useAlphanumeric = config.email.verification.useAlphanumeric;
const chars = useAlphanumeric
? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
: "0123456789";
let code = "";
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * chars.length);
code += chars[randomIndex];
}
return code;
}
/**
* 生成邮箱验证码,使用配置的长度
*/
public static generateEmailCode(): string {
return this.generateCode(config.email.verification.codeLength);
}
// 保存验证码
public static saveCode(email: string, code: string): void {
this.codeStore.set(email, {
code,
expires: Date.now() + this.getExpireTime(),
});
}
// 验证验证码
public static verifyCode(email: string, code: string): boolean {
// 验证逻辑实现...
}
// 其他工具方法...
}
4. 邮件服务
邮件服务负责实际发送验证码邮件:
// email.service.ts
class EmailService {
private transporter: nodemailer.Transporter;
constructor() {
this.transporter = nodemailer.createTransport({
host: config.email.host,
port: config.email.port,
secure: config.email.secure,
auth: {
user: config.email.auth.user,
pass: config.email.auth.pass,
},
});
}
// 发送验证码邮件
public async sendVerificationCode(
to: string,
code: string,
subject: string = "您的验证码"
): Promise<boolean> {
const html = `
<div style="max-width: 600px; margin: 0 auto; padding: 20px; font-family: Arial, sans-serif;">
<!-- 精美的HTML邮件模板 -->
<div style="background-color: #fff; padding: 15px; text-align: center;">
<span style="font-size: 24px; font-weight: bold;">${code}</span>
</div>
</div>
`;
return this.sendMail(to, subject, html);
}
}
配置说明
环境变量配置
在.env
文件中添加以下配置:
# 邮件服务基本配置
EMAIL_HOST=smtp.163.com
EMAIL_PORT=465
EMAIL_SECURE=true
EMAIL_USER=你的邮箱@163.com
EMAIL_PASSWORD=客户端授权密码
EMAIL_FROM=系统名称 <你的邮箱@163.com>
# 邮箱验证码配置
EMAIL_CODE_EXPIRE_MINUTES=10 # 验证码过期时间(分钟)
EMAIL_CODE_LENGTH=6 # 验证码长度
EMAIL_MAX_VERIFY_ATTEMPTS=5 # 同一验证码最大验证尝试次数
EMAIL_MAX_DAILY_SEND=10 # 同一邮箱24小时内最大发送次数
EMAIL_SEND_COOLDOWN=60 # 同一邮箱两次发送之间的最小间隔(秒)
EMAIL_USE_ALPHANUMERIC=false # 是否使用字母数字混合验证码(true/false)
EMAIL_CODE_CASE_SENSITIVE=false # 验证时是否区分大小写(true/false)
配置对象说明
在统一的配置文件中添加邮箱验证码相关配置:
// config/index.ts
const config = {
// ... 其他配置项
email: {
host: process.env.EMAIL_HOST || "smtp.example.com",
port: parseInt(process.env.EMAIL_PORT || "587", 10),
secure: process.env.EMAIL_SECURE === "true",
auth: {
user: process.env.EMAIL_USER || "user@example.com",
pass: process.env.EMAIL_PASSWORD || "password",
},
from: process.env.EMAIL_FROM || "Chat System <no-reply@chatsystem.com>",
verification: {
// 验证码过期时间(分钟),默认10分钟
codeExpireMinutes: parseInt(
process.env.EMAIL_CODE_EXPIRE_MINUTES || "10",
10
),
// 验证码长度,默认6位
codeLength: parseInt(process.env.EMAIL_CODE_LENGTH || "6", 10),
// 同一邮箱最大验证尝试次数,超过则需重新发送验证码,默认5次
maxAttempts: parseInt(process.env.EMAIL_MAX_VERIFY_ATTEMPTS || "5", 10),
// 同一邮箱24小时内最大发送次数,防止滥用,默认10次
maxDailySendCount: parseInt(process.env.EMAIL_MAX_DAILY_SEND || "10", 10),
// 发送频率限制(秒),同一邮箱两次发送之间的最小间隔,默认60秒
sendCooldownSeconds: parseInt(
process.env.EMAIL_SEND_COOLDOWN || "60",
10
),
// 是否使用字母数字混合验证码,默认false(纯数字)
useAlphanumeric: process.env.EMAIL_USE_ALPHANUMERIC === "true",
// 是否区分大小写,默认false
caseSensitive: process.env.EMAIL_CODE_CASE_SENSITIVE === "true",
},
},
};
使用 163 邮箱配置
要使用 163 邮箱配置邮件服务,需要:
登录 163 邮箱并开启 SMTP 服务
生成专用的"客户端授权密码"(非登录密码)
在
.env
文件中配置相应参数,推荐设置如下:EMAIL_HOST=smtp.163.com
EMAIL_PORT=465
EMAIL_SECURE=true
API 接口
发送验证码
POST /email-verification/send
Content-Type: application/json
{
"email": "user@example.com"
}
验证验证码
POST /email-verification/verify
Content-Type: application/json
{
"email": "user@example.com",
"code": "123456"
}
安全性考虑
验证码有效期:设置为 10 分钟,防止长时间有效造成安全风险
邮箱格式验证:控制器层进行格式校验,防止恶意请求
错误处理:完善的异常捕获和错误返回
环境变量:敏感信息如邮箱密码通过环境变量配置
验证尝试次数限制:同一验证码最多尝试 5 次,防止暴力破解
发送频率控制:限制同一邮箱的发送频率,防止滥用
每日发送次数限制:限制 24 小时内最大发送次数,防止骚扰
生产环境优化建议
Redis 存储:使用 Redis 替代内存存储验证码,便于分布式部署
限流措施:添加 IP 或用户级别的请求频率限制
日志监控:记录验证码发送和验证情况,便于异常监控
模板多语言:支持多语言邮件模板,提升国际化体验
总结
通过分层架构和 TypeScript 强类型的支持,我们实现了一个功能完善、代码清晰的邮箱验证码系统。这种实现不仅满足了基本的验证码功能需求,还考虑了安全性、可扩展性和生产环境部署等因素。
在实际应用中,可以根据项目需求对验证码长度、有效期、邮件模板等进行个性化调整,或结合短信验证码一起使用,提供更全面的用户验证方案。