安全策略
一小时内只能获取三次,一天内只能获取五次
Redis存储结构
代码展示
import cn.hutool.core.util.RandomUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
public class Test01 {
@Resource
private StringRedisTemplate stringRedisTemplate;
private static final Logger logger = LogManager.getLogger(Test01.class);
//redis前缀
final String k = "YZM:COUNT:123456";
//过期时间
final Long EXPIRETIME = 1L;
//一小时生成的次数
final String HOURCOUNT = "hourCount";
//24时生成的次数
final String DAYCOUNT = "hourCount";
//yzm
final String YZM = "yzm";
//校验一小时内的时间
final String TIME = "time";
//往Redis中存值
public void setYzm(DateTimeFormatter format,Integer hourCountStart,Integer dayCountStart,Boolean sign){
logger.info("setYzm |start |往Redis中存值");
//验证码的生成时间
LocalDateTime now = LocalDateTime.now();
//LocalDateTime转String
String date = format.format(now);
String yzm = RandomUtil.randomNumbers(6);
//发送验证码
logger.info("setYzm |····· |验证码:{}",yzm);
//准备存储数据
HashMap<String, String> yzmInfo = new HashMap<>();
if (true==sign){
yzmInfo.put("time",date); //验证码生成时间
logger.info("setYzm |····· |生成校验时间:{}",date);
}
yzmInfo.put("hourCount",hourCountStart.toString()); //一小时生成的次数
yzmInfo.put("dayCount",dayCountStart.toString()); //24h生成的次数
yzmInfo.put("yzm",yzm); //验证码
//以hash的结果存入Redis中
stringRedisTemplate.opsForHash().putAll(k,yzmInfo);
Boolean expire = stringRedisTemplate.expire(k, Duration.ofDays(EXPIRETIME));
logger.info("setYzm |end |Redis存储情况:{}",expire);
}
//获取校验时间,判断是否在一小时内
public Boolean getTime(Map<Object, Object> redisMap,DateTimeFormatter format ){
logger.info("getTime |start |取校验时间,判断是否在一小时内获取过验证码");
String redisTime = redisMap.get(TIME).toString();
//String转LocalDatetime
LocalDateTime hourTime = LocalDateTime.parse(redisTime, format);
//获取当前时间
LocalDateTime now = LocalDateTime.now();
//计算时间差,==0 代表一小时内的
long betweenHours = ChronoUnit.HOURS.between(hourTime , now );
logger.info("getTime |···· |时间差:{}",betweenHours);
if (betweenHours==0) {
logger.info("getTime |end |结果是true");
return true;
}
logger.info("getTime |end |结果是false");
return false;
}
@Test
public void getYzmPlus(){
Integer hourCountStart = 1; //一小时生成的次数
Integer dayCountStart = 1; //24h生成的次数
//指定日期格式(格式化日期)
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//查看验证码次数
Map<Object, Object> redisMap = stringRedisTemplate.opsForHash().entries(k);
logger.info("验证码:{}",redisMap);
Boolean sign = true;
if (!redisMap.isEmpty()) {
logger.info("验证码不为空,代表24h内不是第一次获取验证码");
Integer hourCount = Integer.valueOf(redisMap.get(HOURCOUNT).toString());
Integer dayCount = Integer.valueOf(redisMap.get(DAYCOUNT).toString());
//判断是否一小时内发过验证码
Boolean resut = getTime(redisMap, format);
if (resut){
if (hourCount==3){
logger.info("一小时内获取验证码次数上限了!");
throw new RuntimeException("一小时内获取验证码次数上限了!");
}
sign = false;
}
if (dayCount == 5){
logger.info("24时内获取验证码次数上限了!");
//返回
throw new RuntimeException("24时内获取验证码次数上限了!");
}
//校验成功,发送验证码并set
setYzm(format,hourCount+=1,dayCount+=1,sign);
}else {
logger.info("验证码为空,代表24h内第一次获取验证码");
//发送验证码并set
setYzm(format,hourCountStart,dayCountStart,sign);
}
}
}