1.首先创建一个vo类存放我们验证码相关信息。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* @Author Lxq
* @Date 2020/12/7 17:30
* @Version 1.0
* <p>
* 存放验证码相关内容,这个对象是放在redis中
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class VerifyCodeVO implements Serializable {
/**
* 验证码
*/
private Integer verifyCode;
/**
* 发送验证码的手机号
*/
private String phoneNumber;
/**
* 最近成功发送时间
*/
private Date lastSendTime;
/**
* 可用次数
*/
private Integer availableTimes;
}
2.当前时间与晚上十二点的秒差
/**
* 当前时间与晚上十二点的秒差
*
* @return
*/
private Long nowTo12ClockTime() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.MILLISECOND, 0);
//当前时间与晚上十二点的秒差
return (calendar.getTimeInMillis() - System.currentTimeMillis()) / 1000;
}
3.发送验证码的具体方法
@Override
public void sendVerificationCode(String phoneNumber) {
if (phoneNumber == null) {
throw new CustomException("手机号码不能为空!");
}
// 从redis中获取手机号码对应对象信息
VerifyCodeVO verifyCodeVO = redisUtils.get(phoneNumber, VerifyCodeVO.class);
if (verifyCodeVO != null) {
long lastSendTime = verifyCodeVO.getLastSendTime().getTime();
long now = System.currentTimeMillis();
if ((now - lastSendTime) / 1000 < 60) {
throw new CustomException("请不要频繁获取验证码!");
}
Integer availableTimes = verifyCodeVO.getAvailableTimes();
if (availableTimes.equals(0)) {
throw new CustomException("今日验证码次数已使用完毕!");
}
// 次数减一
verifyCodeVO.setAvailableTimes(verifyCodeVO.getAvailableTimes() - 1);
// 重新生成新验证码
int random = (int) ((Math.random() * 9 + 1) * 100000);
verifyCodeVO.setVerifyCode(random);
verifyCodeVO.setLastSendTime(new Date());
redisUtils.set(phoneNumber, verifyCodeVO, nowTo12ClockTime());
// 异步发送短信
String id = ArpSmsTemplateEnum.APP_DATA_VERIFICATION_CODE.getId();
Integer parameters = ArpSmsTemplateEnum.APP_DATA_VERIFICATION_CODE.getParameters();
AsyncManager.me().ttlExecute(AsyncMessageFactory.sendBusinessSms(id, new String[]{"+86" + phoneNumber}, new String[]{String.valueOf(random)}, parameters));
} else {
int random = (int) ((Math.random() * 9 + 1) * 100000);
VerifyCodeVO vc = new VerifyCodeVO();
vc.setPhoneNumber(phoneNumber);
vc.setVerifyCode(random);
vc.setLastSendTime(new Date());
vc.setAvailableTimes(9);
redisUtils.set(phoneNumber, vc, nowTo12ClockTime());
// 模板ID
String id = ArpSmsTemplateEnum.APP_DATA_VERIFICATION_CODE.getId();
Integer parameters = ArpSmsTemplateEnum.APP_DATA_VERIFICATION_CODE.getParameters();
AsyncManager.me().ttlExecute(AsyncMessageFactory.sendBusinessSms(id, new String[]{"+86" + phoneNumber}, new String[]{String.valueOf(random)}, parameters));
}
}
备注:短信接口是调用腾讯云接口,自行解决
4.接收验证码接口
@Override
public Integer getVerificationCode(String phoneNumber) {
VerifyCodeVO verifyCodeVO = redisUtils.get(phoneNumber, VerifyCodeVO.class);
if (verifyCodeVO == null) {
throw new CustomException("请先获取手机验证码!");
}
long currentTimeMillis = System.currentTimeMillis();
long lastTime = verifyCodeVO.getLastSendTime().getTime();
// 10 分钟有效期
if ((lastTime + (10 * 60 * 1000)) < currentTimeMillis) {
throw new CustomException("验证码已过期,请重新获取!");
}
return verifyCodeVO.getVerifyCode();
}
附上redis工具类:
import com.alibaba.fastjson.JSON;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @author Lxq
* @version 1.0
* @date 2020/9/23 9:45
* Redis工具类
*/
@Component
public class RedisUtils {
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 默认过期时长,单位:秒
*/
private final static long DEFAULT_EXPIRE = 60 * 60 * 2;
/**
* 不设置过期时长
*/
private final static long NOT_EXPIRE = -1;
public void set(String key, Object value, long expire) {
redisTemplate.opsForValue().set(key, toJson(value));
if (expire != NOT_EXPIRE) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
}
public void set(String key, Object value) {
set(key, value, NOT_EXPIRE);
}
public <T> T get(String key, Class<T> clazz, long expire) {
Object value = redisTemplate.opsForValue().get(key);
if (expire != NOT_EXPIRE) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
return value == null ? null : fromJson(value.toString(), clazz);
}
/**
* 获取value值,并转成对应的class
*
* @param key
* @param clazz
* @param <T>
* @return
*/
public <T> T get(String key, Class<T> clazz) {
return get(key, clazz, DEFAULT_EXPIRE);
}
/**
* 获取value值,并重置过期时间
*
* @param key
* @param expire
* @return
*/
public String get(String key, long expire) {
Object value = redisTemplate.opsForValue().get(key);
if (expire != NOT_EXPIRE) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
return value == null ? null : value.toString();
}
/**
* 获取value值
*
* @param key
* @return
*/
public String get(String key) {
return get(key, NOT_EXPIRE);
}
/**
* 删除key
*
* @param key
*/
public void delete(String key) {
redisTemplate.delete(key);
}
/**
* 批量删除
*
* @param key
*/
public void delete(Collection<String> key) {
redisTemplate.delete(key);
}
/**
* 获取所有主键
*/
public List<String> keys(String pattern) {
List<String> resultList = new ArrayList<>();
Set<String> keys = redisTemplate.keys(pattern);
if (!ObjectUtils.isEmpty(keys)) {
resultList = new ArrayList<>(keys);
}
return resultList;
}
/**
* 获取缓存类型长度
*/
public Integer getKeysSize(String key) {
List<String> list = keys(key + "*");
return list.size();
}
/**
* object 转json数据
*
* @param object
* @return
*/
private String toJson(Object object) {
if (object instanceof Integer || object instanceof Long || object instanceof Float ||
object instanceof Double || object instanceof Boolean || object instanceof String) {
return String.valueOf(object);
}
return JSON.toJSONString(object);
}
/**
* JSON数据,转成Object
*/
public <T> T fromJson(String json, Class<T> clazz) {
return JSON.parseObject(json, clazz);
}
}