zoukankan      html  css  js  c++  java
  • Redis解决“重试次数”场景的实现思路

    很多地方都要用到重试次数限制,不然就会被暴力破解。比如登录密码。

    下面不是完整代码,只是伪代码,提供一个思路。

    第一种(先声明,这样写有个bug)

    import java.text.MessageFormat;
    
    public class Demo {
    
        /**
         * 限制次数
         */
        private static final Integer MAX_TIMES = 5;
        /**
         * 锁定时间(也是key的失效时间)
         */
        private static final Integer LIMIT_TIME = 3;
        /**
         * key
         */
        private static final String LIMIT_TIMES_KEY = "LimitTimesKey:%s";
    
        public void deal(String phone, String password){
            // 用户id
            Long userId = 6815356L;
            // 组装key
            String key = MessageFormat.format(LIMIT_TIMES_KEY, userId);
            // 先获取key对应的value
            String s = redisService.get(key);
            int currentTimes = s != null ? Integer.parseInt(s) : 0;
            // 如果当前次数为[LIMIT_TIMES]次或以上,则抛异常
            if(currentTimes >= MAX_TIMES){
                throw new RuntimeException("请在"+ LIMIT_TIME +"分钟后继续尝试");
            }
    
            // TODO 做其它逻辑,比如登录操作
            Integer code = userService.login(phone, password);
    
            // 比如密码不正确的状态码是10086
            if(code == 10086){
                // 失败次数+1
                int thisTimes = currentTimes + 1;
                String value = String.valueOf(thisTimes);
                
                // 如果小于最大限制
                if(thisTimes < MAX_TIMES){
                    redisService.set(key, value);
                    throw new RuntimeException("原密码错误,还可以重试"+ (MAX_TIMES - thisTimes) +"次");
                }else{
                    redisService.setex(key, LIMIT_TIME*60, value);
                    throw new RuntimeException("原密码错误,请在"+ LIMIT_TIME +"分钟后继续尝试");
                }
            }
    
            // 登陆成功,清理redis
            redisService.del(key);
        }
    }
    

    以上代码思路:

    以上代码有什么问题呢:当失败次数小于最大限制,那里直接set了一个值,没有设置失效时间。如果用户失败了一次就不再尝试了,那么我们设置的key就会永远存在;同时用户在n年后再去登陆,他拥有的重试次数是凌驾于n年前的重试次数之上的,也就是说我今年浪费了1次重试次数,还剩下4次,我明年再重试,我能够重试的次数就不是5而是4了,因为我的重试次数记录一直存在。

    import java.text.MessageFormat;
    
    public class Demo {
    
        /**
         * 限制次数
         */
        private static final Integer MAX_TIMES = 5;
        /**
         * 锁定时间(也是key的失效时间)
         */
        private static final Integer LIMIT_TIME = 3;
        /**
         * key
         */
        private static final String LIMIT_TIMES_KEY = "LimitTimesKey:%s";
    
        public void deal(String phone, String password){
            // 用户id
            Long userId = 6815356L;
            // 组装key
            String key = MessageFormat.format(LIMIT_TIMES_KEY, userId);
            // 先获取key对应的value
            String s = redisService.get(key);
            int currentTimes = s != null ? Integer.parseInt(s) : 0;
            // 如果当前次数为[LIMIT_TIMES]次或以上,则抛异常
            if(currentTimes >= MAX_TIMES){
                throw new RuntimeException("请在"+ LIMIT_TIME +"分钟后继续尝试");
            }
    
            // TODO 做其它逻辑,比如登录操作
            Integer code = userService.login(phone, password);
    
            // 比如密码不正确的状态码是10086
            if(code == 10086){
                // 失败次数+1
                int thisTimes = currentTimes + 1;
                String value = String.valueOf(thisTimes);
                // 设置值,重点是失效时间
                redisService.setex(key, LIMIT_TIME*60, value);
                // 如果小于最大限制
                if(thisTimes < MAX_TIMES){
                    throw new RuntimeException("原密码错误,还可以重试"+ (MAX_TIMES - thisTimes) +"次");
                }else{
                    throw new RuntimeException("原密码错误,请在"+ LIMIT_TIME +"分钟后继续尝试");
                }
            }
    
            // 登陆成功,清理redis
            redisService.del(key);
        }
    }
    

      

    改进之后的思路如下:

  • 相关阅读:
    Github上的英文解释
    快速搭建脚手架的方法
    vue生命周期简介和钩子函数
    vue2.0 路由模式mode="history"的作用
    浅谈vue $mount()
    vue——解决“You may use special comments to disable some warnings. Use // eslint-disable-next-line to ignore the next line. Use /* eslint-disable */ to ignore all warnings in a file. ”
    Vue组件中的父子传值
    URL中的hash(井号)
    大数据-高并发网络基础1
    大数据-6Linux-shell编程
  • 原文地址:https://www.cnblogs.com/LUA123/p/11251452.html
Copyright © 2011-2022 走看看