76 lines
2.5 KiB
Java
76 lines
2.5 KiB
Java
package com.xjhs.findmemerchant.security.sms;
|
||
|
||
import lombok.RequiredArgsConstructor;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||
import org.springframework.stereotype.Service;
|
||
|
||
import java.time.Duration;
|
||
import java.util.Random;
|
||
|
||
@Slf4j
|
||
@Service
|
||
@RequiredArgsConstructor
|
||
public class SmsCodeService {
|
||
|
||
/** Redis key 前缀:sms:code:{scene}:{phone} */
|
||
private static final String SMS_CODE_KEY_PREFIX = "sms:code:";
|
||
|
||
/** 验证码有效期:5 分钟 */
|
||
private static final Duration SMS_CODE_TTL = Duration.ofMinutes(5);
|
||
|
||
private final StringRedisTemplate redisTemplate;
|
||
private final Random random = new Random();
|
||
private Object smsSender; // 你自己的短信通道接口
|
||
|
||
|
||
private String buildKey(String phone, String scene) {
|
||
return SMS_CODE_KEY_PREFIX + scene + ":" + phone;
|
||
}
|
||
|
||
/**
|
||
* 生成 6 位数字验证码
|
||
*/
|
||
private String generateCode() {
|
||
return String.format("%06d", random.nextInt(1_000_000));
|
||
}
|
||
|
||
/**
|
||
* 发送短信验证码:生成 -> 存 Redis -> 调短信通道发出去
|
||
*/
|
||
public void sendVerificationCode(String phone, String scene) {
|
||
var code = generateCode();
|
||
var key = buildKey(phone, scene);
|
||
// 存到 Redis,设置 TTL
|
||
redisTemplate.opsForValue().set(key, code, SMS_CODE_TTL);
|
||
|
||
// TODO:调用你自己的短信通道(阿里云、腾讯云等)
|
||
// smsSender.sendSmsCode(phone, scene, code);
|
||
|
||
// 开发阶段也可以打印一下方便调试
|
||
log.debug("send sms code, phone={},scene={},code={}", phone,scene, code);
|
||
}
|
||
|
||
/**
|
||
* 校验验证码:
|
||
* - key 不存在 => 过期 / 未发送 => 抛 SmsCodeExpiredException
|
||
* - code 不匹配 => 抛 SmsCodeInvalidException
|
||
* - 匹配 => 删除 key(一次性)
|
||
*/
|
||
public void verifyCode(String phone, String scene, String inputCode) throws Exception {
|
||
String key = buildKey(phone, scene);
|
||
String realCode = redisTemplate.opsForValue().get(key);
|
||
if (realCode == null) {
|
||
// 对齐 Go 里的 ErrSMSCodeExpired
|
||
throw new Exception("验证码已过期或未发送");
|
||
}
|
||
|
||
if (!realCode.equals(inputCode)) {
|
||
// 不正确,但不删除,让用户继续尝试(错误次数由 AuthService 控制)
|
||
throw new Exception("验证码错误");
|
||
}
|
||
// 验证通过,删除验证码(一次性)
|
||
redisTemplate.delete(key);
|
||
}
|
||
}
|
||
|