用Redis实现获取验证码,外加安全策略

安全策略

一小时内只能获取三次,一天内只能获取五次

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);

        }
    }

}

使用Redis实现短信验证码的存储是一种常见的做法,以下是一个基本的实现方法: 1. 在用户请求获取验证码时,生成一个随机验证码,并将验证码保存到Redis中,同时设置过期时间为一定时间(如5分钟)。 2. 将验证码发送到用户手机,并将用户手机号与验证码的对应关系也保存到Redis中,同时设置过期时间为一定时间(如5分钟)。 3. 在用户提交验证码时,服务端从Redis获取对应的验证码和手机号,进行比较,判断验证码是否正确。 4. 如果验证码正确,则将Redis中保存的验证码和手机号删除,同时允许用户进行下一步操作。 下面是一个示例代码: ```python import redis # 连接Redis redis_client = redis.Redis(host='localhost', port=6379) # 生成随机验证码,保存到Redis def save_verify_code(phone_number, verify_code): redis_client.set(phone_number, verify_code) redis_client.expire(phone_number, 300) # 设置过期时间为5分钟 # 获取验证码,并从Redis中删除 def get_verify_code(phone_number): verify_code = redis_client.get(phone_number) redis_client.delete(phone_number) return verify_code.decode('utf-8') if verify_code else None # 检查验证码是否正确 def check_verify_code(phone_number, verify_code): return get_verify_code(phone_number) == verify_code ``` 这里使用Redis客户端库`redis`,首先连接到本地的Redis服务器(默认端口为6379),然后定义了三个函数:`save_verify_code`用于保存验证码,`get_verify_code`用于获取验证码并删除,`check_verify_code`用于检查验证码是否正确。这些函数都是基于Redis的字符串类型实现的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值