解析在下面
附赠代码
private static class CodeInfo {String code;long timestamp;CodeInfo(String code, long timestamp) {this.code = code;this.timestamp = timestamp;}}
// 存储验证码(邮箱 -> 验证码信息)(保证线程安全) 以免中途更改邮箱private static final ConcurrentHashMap<String, CodeInfo> codeMap = new ConcurrentHashMap<>();
String email = request.getEmail();// 检查邮箱是否发送过验证码if (!codeMap.containsKey(email)) {response = "EMAIL_CHANGED"; // 邮箱被更改或未发送验证码break;}
分步解析:如何完善注册逻辑以检测邮箱中途更换
1. 核心逻辑设计
• 目标:确保用户注册时使用的邮箱必须与发送验证码的邮箱一致,防止中途篡改。
• 关键机制:
• 使用 ConcurrentHashMap<String, CodeInfo>
存储 邮箱 → 验证码信息。
• 注册时检查提交的邮箱是否已发送过验证码(存在于 codeMap
中)。
• 若邮箱未发送过验证码或已被更换,返回明确错误码 EMAIL_CHANGED
。
2. 关键代码解析
2.1 验证码存储结构
private static final ConcurrentHashMap<String, CodeInfo> codeMap = new ConcurrentHashMap<>();
• 键(Key):用户邮箱(唯一标识)。
• 值(Value):CodeInfo
对象,包含验证码和生成时间戳。
• 线程安全:ConcurrentHashMap
确保多线程环境下的安全访问。
2.2 发送验证码逻辑(Case 4)
case 4:String email = request.getEmail();String code = handleSendRegisterCode(request); // 生成并发送验证码codeMap.put(email, new CodeInfo(code, System.currentTimeMillis())); // 存储邮箱与验证码response = "验证码发送成功";break;
• 行为:用户请求发送验证码时,将邮箱和验证码存入 codeMap
。
• 防篡改:后续注册必须使用同一邮箱提交验证码。
2.3 注册逻辑(Case 2)
case 2:String email = request.getEmail();String inputCode = request.getVerification();// 检查邮箱是否发送过验证码if (!codeMap.containsKey(email)) {response = "EMAIL_CHANGED"; // 邮箱被中途更换或未发送验证码break;}// 验证验证码是否正确或过期if (!validateCode(email, inputCode)) {response = "验证码错误或已过期";break;}// 执行注册...break;
• 关键检查:codeMap.containsKey(email)
确保注册邮箱必须与发送验证码的邮箱一致。
• 错误处理:若邮箱不一致,返回 EMAIL_CHANGED
。
3. 验证码验证方法
private boolean validateCode(String email, String inputCode) {CodeInfo storedInfo = codeMap.get(email);if (storedInfo == null) return false; // 邮箱未发送过验证码boolean expired = (System.currentTimeMillis() - storedInfo.timestamp) > 300_000; // 5分钟过期boolean valid = storedInfo.code.equalsIgnoreCase(inputCode); // 不区分大小写if (valid) codeMap.remove(email); // 验证成功移除记录return !expired && valid;
}
• 行为:
• 检查验证码是否过期(5分钟)。
• 检查验证码是否匹配。
• 验证成功后移除记录,防止重复使用。
4. 防止中途更换邮箱的流程
- 用户操作:
• 输入邮箱 A → 发送验证码 → 后端存储A → CodeInfo
。 • 修改邮箱为 B → 直接提交注册(未重新发送验证码)。 - 后端检测:
• 注册时检查codeMap.containsKey(B)
→ 不存在。 • 返回错误码EMAIL_CHANGED
。 - 前端提示:
else if ("EMAIL_CHANGED".equals(response)) {showErrorAlert("检测到邮箱已更改,请重新获取验证码");
}
5. 测试场景验证
场景 | 后端行为 | 前端提示 |
---|---|---|
用户未发送验证码直接注册 | codeMap 无记录 → 返回 EMAIL_CHANGED | “检测到邮箱已更改,请重新获取验证码” |
用户发送验证码后更改邮箱未重发 | 新邮箱不在 codeMap → 返回 EMAIL_CHANGED | “检测到邮箱已更改,请重新获取验证码” |
用户发送验证码后未更改邮箱 | 邮箱存在于 codeMap → 继续验证码检查 → 注册成功 | “注册成功” |
6. 潜在问题与优化
- 问题:用户可能通过并发请求绕过验证码发送限制。
优化:在发送验证码时增加频率限制(如1分钟内同一邮箱只能发送一次)。 - 问题:
codeMap
在分布式环境下无法共享。
优化:改用 Redis 存储验证码,支持分布式和自动过期。 - 问题:前端未正确传递邮箱参数。
调试:添加日志输出,确认后端收到的邮箱与发送验证码时一致。
7. 最终总结
• 完善逻辑:通过 codeMap
的键绑定和注册时的邮箱检查,有效防止中途更换邮箱。
• 错误处理:明确返回 EMAIL_CHANGED
,提升用户体验。
• 扩展性:当前方案适用于单机环境,分布式场景需升级存储方案。