6
pom.xml
6
pom.xml
@@ -159,6 +159,12 @@
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis 依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
@@ -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, "系统异常"),
|
||||
/** 操作失败 */
|
||||
|
||||
@@ -149,3 +149,4 @@ public class SmsUtil {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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<String> 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<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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("=====================================");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,3 +75,4 @@ public class EmailAndSmsTest {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user