# 邮箱验证双重验证实现计划 ## 需求总结 在找回密码功能中,用户可能使用虚假邮箱(如 `test@example.com`)导致邮件被退回。需要在发送前验证邮箱真实性,避免无效发送。 ## 当前状态分析 ### 前端(ForgotPassword.tsx) - 仅验证邮箱是否为空 - 无格式验证 - 无"必须使用真实邮箱"提示 ### 后端(user/views/user.py) - `ForgotPasswordSendCodeAPIView` 仅验证邮箱是否为空 - 直接调用 `FUser.objects.filter(email=to_email).first()` 查询用户 - 若用户不存在,返回 `USER_NOT_FOUND` 错误 - 无 MX 记录验证 ## 实现方案 ### 1. 前端验证 **文件**:`src/pages/ForgotPassword/ForgotPassword.tsx` **修改内容**: - 增加邮箱格式验证(使用正则表达式) - 发送前弹出确认提示:"请确保使用真实邮箱,验证码将发送至该邮箱" - 前端验证不通过时不发送请求 ```tsx const handleSendCode = async () => { if (!email) { message.warning(t('forgotPassword.warnEmailRequired')); return; } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { message.warning(t('forgotPassword.warnEmailFormat')); return; } // 继续发送... }; ``` **新增翻译文本**: - `warnEmailFormat`: "邮箱格式不正确" - `confirmSend`: "验证码将发送至该邮箱,请确认是真实邮箱" ### 2. 后端 MX 记录验证 **文件**:`user/views/user.py` **修改内容**: - 在 `ForgotPasswordSendCodeAPIView.post()` 中增加 MX 记录验证 - 使用 Python `socket` 或 `dns.resolver` 库查询 MX 记录 - 若 MX 记录不存在,直接返回错误 ```python import socket def validate_email_mx(email): """验证邮箱域名是否有MX记录""" try: domain = email.split('@')[1] mx_records = socket.getaddrinfo(domain, None, socket.AF_INET, socket.SOCK_STREAM) return len(mx_records) > 0 except (socket.gaierror, IndexError): return False ``` **注意**:QQ Mail 对无效域名会退信,MX 验证可以在发送前拦截。 ### 3. 涉及文件 | 文件 | 修改类型 | 说明 | |------|----------|------| | `src/pages/ForgotPassword/ForgotPassword.tsx` | 修改 | 增加格式验证和提示 | | `user/views/user.py` | 修改 | 增加 MX 记录验证 | | `src/locales/zh.json` | 修改 | 添加错误提示文本 | ## 验证步骤 1. **前端格式验证** - 输入 `test`(无@符号)→ 提示格式错误 - 输入 `test@example.com` → 继续验证 - 输入 `test@nonexistent-domain-xyz.com` → 后端MX验证拦截 2. **后端 MX 验证** - 输入 `test@example.com` → MX 不存在,返回错误 - 输入 `test@qq.com` → MX 存在,继续发送 - 输入 `test@gmail.com` → MX 存在,继续发送 3. **端到端测试** - 使用真实邮箱 `test@qq.com` 测试完整流程 - 确认收到验证码邮件 ## 备选方案 如果 MX 验证过于严格(可能误杀某些特殊域名),可以考虑: - 仅在前端提示,不拦截 - 或添加白名单机制 - 或使用第三方邮箱验证 API ## 注意事项 - MX 验证需要 DNS 查询,可能增加约 100ms 延迟 - 建议结合缓存避免重复查询 - 仅对找回密码等关键场景启用