77 lines
2.5 KiB
Java
77 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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|