feat: 注册、重置密码、重发验证邮件添加验证码功能
后端修改: - 添加通用验证码验证函数 verifyCaptcha() - /api/register 接口添加验证码验证 - /api/password/forgot 接口添加验证码验证 - /api/resend-verification 接口添加验证码验证 前端修改: - 注册表单添加验证码输入框和图片 - 忘记密码模态框添加验证码 - 重发验证邮件区域添加验证码输入 - 添加各表单的验证码刷新方法 - 提交失败后自动刷新验证码 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1100,6 +1100,42 @@ function checkMailRateLimit(req, type = 'mail') {
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 验证码验证辅助函数 =====
|
||||
|
||||
/**
|
||||
* 验证验证码
|
||||
* @param {Object} req - 请求对象
|
||||
* @param {string} captcha - 用户输入的验证码
|
||||
* @returns {{valid: boolean, message?: string}} 验证结果
|
||||
*/
|
||||
function verifyCaptcha(req, captcha) {
|
||||
if (!captcha) {
|
||||
return { valid: false, message: '请输入验证码' };
|
||||
}
|
||||
|
||||
const sessionCaptcha = req.session.captcha;
|
||||
const captchaTime = req.session.captchaTime;
|
||||
|
||||
if (!sessionCaptcha || !captchaTime) {
|
||||
return { valid: false, message: '验证码已过期,请刷新验证码' };
|
||||
}
|
||||
|
||||
// 验证码有效期5分钟
|
||||
if (Date.now() - captchaTime > 5 * 60 * 1000) {
|
||||
return { valid: false, message: '验证码已过期,请刷新验证码' };
|
||||
}
|
||||
|
||||
if (captcha.toLowerCase() !== sessionCaptcha) {
|
||||
return { valid: false, message: '验证码错误' };
|
||||
}
|
||||
|
||||
// 验证通过后清除session中的验证码
|
||||
delete req.session.captcha;
|
||||
delete req.session.captchaTime;
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
// ===== 公开API =====
|
||||
|
||||
// 健康检查
|
||||
@@ -1164,7 +1200,8 @@ app.post('/api/register',
|
||||
.isLength({ min: 3, max: 20 }).withMessage('用户名长度3-20个字符')
|
||||
.matches(USERNAME_REGEX).withMessage('用户名仅允许中英文、数字、下划线、点和短横线'),
|
||||
body('email').isEmail().withMessage('邮箱格式不正确'),
|
||||
body('password').isLength({ min: 6 }).withMessage('密码至少6个字符')
|
||||
body('password').isLength({ min: 6 }).withMessage('密码至少6个字符'),
|
||||
body('captcha').notEmpty().withMessage('请输入验证码')
|
||||
],
|
||||
async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
@@ -1176,6 +1213,16 @@ app.post('/api/register',
|
||||
}
|
||||
|
||||
try {
|
||||
// 验证验证码
|
||||
const { captcha } = req.body;
|
||||
const captchaResult = verifyCaptcha(req, captcha);
|
||||
if (!captchaResult.valid) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: captchaResult.message
|
||||
});
|
||||
}
|
||||
|
||||
checkMailRateLimit(req, 'verify');
|
||||
const { username, email, password } = req.body;
|
||||
|
||||
@@ -1263,7 +1310,8 @@ app.post('/api/resend-verification', [
|
||||
body('username')
|
||||
.optional({ checkFalsy: true })
|
||||
.isLength({ min: 3 }).withMessage('用户名格式不正确')
|
||||
.matches(USERNAME_REGEX).withMessage('用户名仅允许中英文、数字、下划线、点和短横线')
|
||||
.matches(USERNAME_REGEX).withMessage('用户名仅允许中英文、数字、下划线、点和短横线'),
|
||||
body('captcha').notEmpty().withMessage('请输入验证码')
|
||||
], async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
@@ -1271,6 +1319,16 @@ app.post('/api/resend-verification', [
|
||||
}
|
||||
|
||||
try {
|
||||
// 验证验证码
|
||||
const { captcha } = req.body;
|
||||
const captchaResult = verifyCaptcha(req, captcha);
|
||||
if (!captchaResult.valid) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: captchaResult.message
|
||||
});
|
||||
}
|
||||
|
||||
checkMailRateLimit(req, 'verify');
|
||||
|
||||
const { email, username } = req.body;
|
||||
@@ -1332,15 +1390,25 @@ app.get('/api/verify-email', async (req, res) => {
|
||||
|
||||
// 发起密码重置(邮件)
|
||||
app.post('/api/password/forgot', [
|
||||
body('email').isEmail().withMessage('邮箱格式不正确')
|
||||
body('email').isEmail().withMessage('邮箱格式不正确'),
|
||||
body('captcha').notEmpty().withMessage('请输入验证码')
|
||||
], async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ success: false, errors: errors.array() });
|
||||
}
|
||||
|
||||
const { email } = req.body;
|
||||
const { email, captcha } = req.body;
|
||||
try {
|
||||
// 验证验证码
|
||||
const captchaResult = verifyCaptcha(req, captcha);
|
||||
if (!captchaResult.valid) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: captchaResult.message
|
||||
});
|
||||
}
|
||||
|
||||
checkMailRateLimit(req, 'pwd_forgot');
|
||||
|
||||
const smtpConfig = getSmtpConfig();
|
||||
|
||||
Reference in New Issue
Block a user