From eb2b0cb999db2415d4086b177e27de281c78b0ab Mon Sep 17 00:00:00 2001 From: bao <19271189822@163.com> Date: Mon, 5 Jan 2026 10:38:29 +0800 Subject: [PATCH] =?UTF-8?q?=E9=82=AE=E7=AE=B1=E9=AA=8C=E8=AF=81=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 + .../com/bao/dating/common/ResultCode.java | 8 ++ .../com/bao/dating/common/aliyun/SmsUtil.java | 1 + .../VerificationCodeController.java | 90 +++++++++++++ .../service/VerificationCodeService.java | 31 +++++ .../impl/VerificationCodeServiceImpl.java | 122 ++++++++++++++++++ .../java/com/bao/dating/util/MD5Util.java | 93 ------------- .../service/VerificationCodeServiceTest.java | 91 +++++++++++++ .../com/bao/dating/util/EmailAndSmsTest.java | 1 + 9 files changed, 350 insertions(+), 93 deletions(-) create mode 100644 src/main/java/com/bao/dating/controller/VerificationCodeController.java create mode 100644 src/main/java/com/bao/dating/service/VerificationCodeService.java create mode 100644 src/main/java/com/bao/dating/service/impl/VerificationCodeServiceImpl.java create mode 100644 src/test/java/com/bao/dating/service/VerificationCodeServiceTest.java diff --git a/pom.xml b/pom.xml index 278ec98..2079d64 100644 --- a/pom.xml +++ b/pom.xml @@ -148,6 +148,12 @@ spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-data-redis + + diff --git a/src/main/java/com/bao/dating/common/ResultCode.java b/src/main/java/com/bao/dating/common/ResultCode.java index 2c7b892..9da6ee0 100644 --- a/src/main/java/com/bao/dating/common/ResultCode.java +++ b/src/main/java/com/bao/dating/common/ResultCode.java @@ -19,6 +19,14 @@ public enum ResultCode { FORBIDDEN(403, "无权限"), /** 数据不存在 */ NOT_FOUND(404, "数据不存在"), + /** 验证码已发送 */ + CODE_SENT(200, "验证码已发送"), + /** 验证码错误 */ + CODE_ERROR(400, "验证码错误"), + /** 验证码已过期 */ + CODE_EXPIRED(400, "验证码已过期"), + /** 验证码发送失败 */ + CODE_SEND_FAIL(500, "验证码发送失败"), /** 系统异常 */ SYSTEM_ERROR(500, "系统异常"), /** 操作失败 */ diff --git a/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java b/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java index b09f8bc..290c766 100644 --- a/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java +++ b/src/main/java/com/bao/dating/common/aliyun/SmsUtil.java @@ -149,3 +149,4 @@ public class SmsUtil { + diff --git a/src/main/java/com/bao/dating/controller/VerificationCodeController.java b/src/main/java/com/bao/dating/controller/VerificationCodeController.java new file mode 100644 index 0000000..6d5084a --- /dev/null +++ b/src/main/java/com/bao/dating/controller/VerificationCodeController.java @@ -0,0 +1,90 @@ +package com.bao.dating.controller; + +import com.bao.dating.common.Result; +import com.bao.dating.common.ResultCode; +import com.bao.dating.service.VerificationCodeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +/** + * 验证码控制器 + * @author KilLze + */ +@Slf4j +@RestController +@RequestMapping("/api/verification") +public class VerificationCodeController { + + @Autowired + private VerificationCodeService verificationCodeService; + + /** + * 发送邮箱验证码 + * @param email 邮箱地址 + * @return 结果 + */ + @PostMapping("/send-email-code") + public Result sendEmailCode(@RequestParam String email) { + // 参数校验 + if (!StringUtils.hasText(email)) { + return Result.error(ResultCode.PARAM_ERROR, "邮箱地址不能为空"); + } + + // 简单的邮箱格式校验 + if (!isValidEmail(email)) { + return Result.error(ResultCode.PARAM_ERROR, "邮箱格式不正确"); + } + + // 发送验证码 + boolean success = verificationCodeService.sendEmailCode(email); + + if (success) { + return Result.success(ResultCode.CODE_SENT, "验证码已发送到您的邮箱,请查收"); + } else { + return Result.error(ResultCode.CODE_SEND_FAIL, "验证码发送失败,请稍后重试"); + } + } + + /** + * 验证邮箱验证码 + * @param email 邮箱地址 + * @param code 验证码 + * @return 结果 + */ + @PostMapping("/verify-email-code") + public Result verifyEmailCode(@RequestParam String email, @RequestParam String code) { + // 参数校验 + if (!StringUtils.hasText(email)) { + return Result.error(ResultCode.PARAM_ERROR, "邮箱地址不能为空"); + } + + if (!StringUtils.hasText(code)) { + return Result.error(ResultCode.PARAM_ERROR, "验证码不能为空"); + } + + // 验证验证码 + boolean success = verificationCodeService.verifyEmailCode(email, code); + + if (success) { + return Result.success(ResultCode.SUCCESS, "验证码验证成功"); + } else { + return Result.error(ResultCode.CODE_ERROR, "验证码错误或已过期"); + } + } + + /** + * 简单的邮箱格式校验 + * @param email 邮箱地址 + * @return 是否有效 + */ + private boolean isValidEmail(String email) { + if (email == null || email.trim().isEmpty()) { + return false; + } + // 简单的邮箱格式校验:包含@和. + return email.contains("@") && email.contains(".") && email.length() > 5; + } +} + diff --git a/src/main/java/com/bao/dating/service/VerificationCodeService.java b/src/main/java/com/bao/dating/service/VerificationCodeService.java new file mode 100644 index 0000000..81bcd76 --- /dev/null +++ b/src/main/java/com/bao/dating/service/VerificationCodeService.java @@ -0,0 +1,31 @@ +package com.bao.dating.service; + +/** + * 验证码服务接口 + * @author KilLze + */ +public interface VerificationCodeService { + + /** + * 发送邮箱验证码 + * @param email 邮箱地址 + * @return 是否发送成功 + */ + boolean sendEmailCode(String email); + + /** + * 验证邮箱验证码 + * @param email 邮箱地址 + * @param code 验证码 + * @return 是否验证成功 + */ + boolean verifyEmailCode(String email, String code); + + /** + * 生成验证码 + * @param length 验证码长度(默认6位) + * @return 验证码字符串 + */ + String generateCode(int length); +} + diff --git a/src/main/java/com/bao/dating/service/impl/VerificationCodeServiceImpl.java b/src/main/java/com/bao/dating/service/impl/VerificationCodeServiceImpl.java new file mode 100644 index 0000000..19bae75 --- /dev/null +++ b/src/main/java/com/bao/dating/service/impl/VerificationCodeServiceImpl.java @@ -0,0 +1,122 @@ +package com.bao.dating.service.impl; + +import com.bao.dating.service.VerificationCodeService; +import com.bao.dating.util.EmailUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * 验证码服务实现类 + * @author KilLze + */ +@Slf4j +@Service +public class VerificationCodeServiceImpl implements VerificationCodeService { + + @Autowired + private EmailUtil emailUtil; + + @Autowired + private StringRedisTemplate redisTemplate; + + /** + * Redis中验证码的key前缀 + */ + private static final String CODE_KEY_PREFIX = "email:code:"; + + /** + * 验证码过期时间(分钟) + */ + private static final long CODE_EXPIRE_MINUTES = 10; + + /** + * 验证码长度 + */ + private static final int CODE_LENGTH = 6; + + /** + * 发送邮箱验证码 + * @param email 邮箱地址 + * @return 是否发送成功 + */ + @Override + public boolean sendEmailCode(String email) { + try { + // 生成验证码 + String code = generateCode(CODE_LENGTH); + + // 存储到Redis,设置过期时间 + String key = CODE_KEY_PREFIX + email; + redisTemplate.opsForValue().set(key, code, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES); + + // 发送邮件 + boolean sendResult = emailUtil.sendVerificationCode(email, code); + + if (sendResult) { + log.info("邮箱验证码发送成功,邮箱:{},验证码:{}", email, code); + return true; + } else { + // 如果发送失败,删除Redis中的验证码 + redisTemplate.delete(key); + log.error("邮箱验证码发送失败,邮箱:{}", email); + return false; + } + } catch (Exception e) { + log.error("发送邮箱验证码异常,邮箱:{},异常信息:{}", email, e.getMessage(), e); + return false; + } + } + + /** + * 验证邮箱验证码 + * @param email 邮箱地址 + * @param code 验证码 + * @return 是否验证成功 + */ + @Override + public boolean verifyEmailCode(String email, String code) { + try { + String key = CODE_KEY_PREFIX + email; + String storedCode = redisTemplate.opsForValue().get(key); + + if (storedCode == null) { + log.warn("验证码不存在或已过期,邮箱:{}", email); + return false; + } + + if (storedCode.equals(code)) { + // 验证成功后,删除验证码(防止重复使用) + redisTemplate.delete(key); + log.info("邮箱验证码验证成功,邮箱:{}", email); + return true; + } else { + log.warn("邮箱验证码错误,邮箱:{},输入的验证码:{},正确的验证码:{}", email, code, storedCode); + return false; + } + } catch (Exception e) { + log.error("验证邮箱验证码异常,邮箱:{},异常信息:{}", email, e.getMessage(), e); + return false; + } + } + + /** + * 生成验证码 + * @param length 验证码长度 + * @return 验证码字符串 + */ + @Override + public String generateCode(int length) { + Random random = new Random(); + StringBuilder code = new StringBuilder(); + for (int i = 0; i < length; i++) { + code.append(random.nextInt(10)); // 生成0-9的随机数字 + } + return code.toString(); + } +} + diff --git a/src/main/java/com/bao/dating/util/MD5Util.java b/src/main/java/com/bao/dating/util/MD5Util.java index 1e8cfc7..e69de29 100644 --- a/src/main/java/com/bao/dating/util/MD5Util.java +++ b/src/main/java/com/bao/dating/util/MD5Util.java @@ -1,93 +0,0 @@ -package com.bao.dating.util; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -/** - * MD5工具类 - * 提供MD5加密功能 - * @author KilLze - */ -public class MD5Util { - - /** - * 对字符串进行MD5加密 - * @param input 待加密的字符串 - * @return MD5加密后的32位小写字符串 - */ - public static String encrypt(String input) { - if (input == null || input.isEmpty()) { - return null; - } - - try { - MessageDigest md = MessageDigest.getInstance("MD5"); - byte[] digest = md.digest(input.getBytes()); - return bytesToHex(digest); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("MD5算法不可用", e); - } - } - - /** - * 对字符串进行MD5加密(带盐值) - * @param input 待加密的字符串 - * @param salt 盐值 - * @return MD5加密后的32位小写字符串 - */ - public static String encryptWithSalt(String input, String salt) { - if (input == null || input.isEmpty()) { - return null; - } - if (salt == null) { - salt = ""; - } - return encrypt(input + salt); - } - - /** - * 验证字符串与MD5值是否匹配 - * @param input 原始字符串 - * @param md5Hash MD5哈希值 - * @return 是否匹配 - */ - public static boolean verify(String input, String md5Hash) { - if (input == null || md5Hash == null) { - return false; - } - return encrypt(input).equalsIgnoreCase(md5Hash); - } - - /** - * 验证字符串与MD5值是否匹配(带盐值) - * @param input 原始字符串 - * @param salt 盐值 - * @param md5Hash MD5哈希值 - * @return 是否匹配 - */ - public static boolean verifyWithSalt(String input, String salt, String md5Hash) { - if (input == null || md5Hash == null) { - return false; - } - if (salt == null) { - salt = ""; - } - return encryptWithSalt(input, salt).equalsIgnoreCase(md5Hash); - } - - /** - * 将字节数组转换为十六进制字符串 - * @param bytes 字节数组 - * @return 十六进制字符串 - */ - private static String bytesToHex(byte[] bytes) { - StringBuilder sb = new StringBuilder(); - for (byte b : bytes) { - sb.append(String.format("%02x", b)); - } - return sb.toString(); - } -} - - - diff --git a/src/test/java/com/bao/dating/service/VerificationCodeServiceTest.java b/src/test/java/com/bao/dating/service/VerificationCodeServiceTest.java new file mode 100644 index 0000000..b809304 --- /dev/null +++ b/src/test/java/com/bao/dating/service/VerificationCodeServiceTest.java @@ -0,0 +1,91 @@ +package com.bao.dating.service; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * 验证码服务测试类 + * @author KilLze + */ +@SpringBootTest +public class VerificationCodeServiceTest { + + @Autowired + private VerificationCodeService verificationCodeService; + + /** + * 测试发送邮箱验证码 + */ + @Test + public void testSendEmailCode() { + String email = "test@example.com"; // 请修改为实际邮箱地址 + + System.out.println("========== 发送邮箱验证码测试 =========="); + System.out.println("邮箱地址: " + email); + + boolean result = verificationCodeService.sendEmailCode(email); + + System.out.println("发送结果: " + (result ? "成功" : "失败")); + System.out.println("====================================="); + } + + /** + * 测试验证邮箱验证码 + */ + @Test + public void testVerifyEmailCode() { + String email = "test@example.com"; // 请修改为实际邮箱地址 + String code = "123456"; // 请修改为实际收到的验证码 + + System.out.println("========== 验证邮箱验证码测试 =========="); + System.out.println("邮箱地址: " + email); + System.out.println("验证码: " + code); + + boolean result = verificationCodeService.verifyEmailCode(email, code); + + System.out.println("验证结果: " + (result ? "成功" : "失败")); + System.out.println("====================================="); + } + + /** + * 测试完整流程:发送验证码 -> 验证验证码 + */ + @Test + public void testCompleteFlow() { + String email = "test@example.com"; // 请修改为实际邮箱地址 + + System.out.println("========== 完整流程测试 =========="); + System.out.println("邮箱地址: " + email); + + // 1. 发送验证码 + System.out.println("\n1. 发送验证码..."); + boolean sendResult = verificationCodeService.sendEmailCode(email); + System.out.println("发送结果: " + (sendResult ? "成功" : "失败")); + + if (sendResult) { + // 2. 等待用户输入验证码(这里模拟,实际应该从控制台或API获取) + System.out.println("\n2. 请查看邮箱获取验证码,然后手动测试验证功能"); + System.out.println(" 使用 testVerifyEmailCode() 方法进行验证"); + } + + System.out.println("\n====================================="); + } + + /** + * 测试生成验证码 + */ + @Test + public void testGenerateCode() { + System.out.println("========== 生成验证码测试 =========="); + + // 测试不同长度的验证码 + for (int length = 4; length <= 8; length++) { + String code = verificationCodeService.generateCode(length); + System.out.println(length + "位验证码: " + code); + } + + System.out.println("====================================="); + } +} + diff --git a/src/test/java/com/bao/dating/util/EmailAndSmsTest.java b/src/test/java/com/bao/dating/util/EmailAndSmsTest.java index 55014bf..86ced71 100644 --- a/src/test/java/com/bao/dating/util/EmailAndSmsTest.java +++ b/src/test/java/com/bao/dating/util/EmailAndSmsTest.java @@ -75,3 +75,4 @@ public class EmailAndSmsTest { +