阿里面试官问我:如何设计登录接口,十分钟内连续登录5次失败,需要等待30分钟才能登录

  常言道:字数越短问题越大。

  今天阿里的面试官小哥哥让我实现一个登录接口,同一个用户10分钟内连续登陆5次失败,则需要等到30分钟才能登陆

  当然大佬估计一看到这种题目会很难过,一丁点算法都没有,妙解没意思。我这个小菜鸟上来就被唬住了。登录接口?10分钟内连续5次??等待30分钟才能登陆???登陆验证????

  问号一下子就冒出来了,当然最开始我想定义一个变量firstFailTime来记录第一次失败的时间,再仔细一想不对啊,firstFailTime是动态的额,要不断变化,单一个变量不好实现啊,第一次登录失败可以记录,但如果出现前十分钟失败了4次,第11分钟又失败了一次的话,firstFailTime应该往后取第二次失败登录的时间啊,我总不能手动定义100个变量吧。。。面试官看到估计脸都绿了。恨不得给我一个Mysql数据表,把每次登陆都给存下来,这样就可以很方便的查出某个时间区间登陆的情况。

  不慌,咱们虽然不是大佬,但一点一点分析还是可以的,沉住气!等等,刚刚说到数据库存所有的登录数据??其实思考到上面已经快接近了,我不能手动创建100个变量,但我可以用一种数据结构依次记录登录失败的时间啊,突然想到LRU算法对不对!!能从数据顺序看出来时间顺序的数据结构不就是链表吗!!!还有登录验证的问题,不如偷个懒,用一个boolean控制。解决,cool~

P.S:这只是我的思路,肯定还有更好的解决方案,跪求评论区留言。

P.S:我没考虑开多个线程去测试,因为我个人感觉用户登录不会出现在高并发的环境里,几万个人同时登陆同一个账号想想就离谱......但为了保险起见我还是给map加了synchronize关键字。


Person类:

package exam;

import java.util.LinkedList;

/**
 * Created by Enzo Cotter on 2021/3/10.
 */
public class Person {
    /**
     * 重置时间
     */
    private static final int RESET_TIME = 30;

    /**
     * 密码连续输入5次失败的持续时间
     */
    private static final int DURATION = 10;

    /**
     * 最大输入失败次数
     */
    private static final int MAX_TIMES = 5;

    /**
     * 用户id
     */
    private String id;

    /**
     * 登录失败次数
     */
    private int failCount;

    /**
     * 第一次失败的时间
     */
    private long firstFailTime;

    /**
     * 登录失败的时间
     */
    private LinkedList<Long> times;

    private boolean lock;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getFailCount() {
        return failCount;
    }

    public void setFailCount(int failCount) {
        this.failCount = failCount;
    }

    public long getFirstFailTime() {
        return firstFailTime;
    }

    public void setFirstFailTime(long firstFailTime) {
        this.firstFailTime = firstFailTime;
    }

    public LinkedList<Long> getTimes() {
        return times;
    }

    public void setTimes(LinkedList<Long> times) {
        this.times = times;
    }

    public Person() {
    }

    public Person(String id, int failCount, long firstFailTime, LinkedList<Long> times, boolean lock) {
        this.id = id;
        this.failCount = failCount;
        this.firstFailTime = firstFailTime;
        this.times = times;
        this.lock = false;
    }

    /**
     * 密码输错了进入此方法
     */
    public void isValid(){

        long thisTime = System.currentTimeMillis() / 1000;

        System.out.println("第一次登录失败时间" + thisTime);

        // 超过30分钟,重置
        if(thisTime > firstFailTime + RESET_TIME){
            this.failCount = 1;
            firstFailTime = thisTime;
            times = new LinkedList<>();
            times.addLast(thisTime);
            this.lock = false;
            return;
        }else{ // 没有超过30分钟

            if (lock){
                System.out.println("账户锁定,请" + RESET_TIME + "分钟后再来");
                return;
            }

            // 之前记录的第一次登录失败时间在10分钟之前了,要换
            while(!times.isEmpty() && thisTime > times.getFirst() + DURATION){
                times.removeFirst();
                this.failCount --;
                this.firstFailTime = times.isEmpty() ? thisTime : times.getFirst();
            }

            if(this.failCount >= 5 && thisTime < firstFailTime + DURATION){
                System.out.println("10分钟内密码错误大于等于5次,登录失败");
                times.addLast(thisTime);
                this.lock = true;
            }else if(failCount < MAX_TIMES){
                this.failCount ++;
                System.out.println("密码错误" + this.failCount + "次");
                times.addLast(thisTime);
            }
        }
    }
}
复制代码

主类:

package exam;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * Created by Enzo Cotter on 2021/3/10.
 */
public class FlowLimit {

    private static Map<String, Person> map = new HashMap<>();

    /**
     * 登录
     * @param id
     * @param flag  是否成功
     */
    public static void login(String id, boolean flag){
        if (flag){
            // 登陆成功
            return;
        }else{
            Person p = null;
            // 登录失败
            synchronized (map) {
                p = map.get(id);
                if (p == null){
                    p = new Person(id, 0, System.currentTimeMillis() / 1000,
                            new LinkedList<>(), false);
                    map.put(id, p);
                    return;
                }
                p.isValid();
            }
        }
    }

    public static void main(String[] args) {
        for(int i = 0; i < 20; i ++){
            login("aaa", false);
        }
    }
}
复制代码

猜你喜欢

转载自juejin.im/post/7028846215228260365