概述
写在前面: 正常业务里的实现不能这样搞,合适的方法是走缓存,比如使用redis,我当时就只有原生Java API能用,请大家把这个当成算法题来看待。
常言道:字数越短问题越大。
今天阿里的面试官小哥哥让我实现一个登录接口,同一个用户10分钟内连续登陆5次失败,则需要等到30分钟才能登陆。
当然大佬估计一看到这种题目会很难过,一丁点算法都没有,妙解没意思。我上来就被唬住了。登录接口?10分钟内连续5次??等待30分钟才能登陆???登陆验证????
问号一下子就冒出来了,当然最开始我想定义一个变量firstFailTime
来记录第一次失败的时间,再仔细一想不对啊,firstFailTime
是动态的额,要不断变化,单一个变量不好实现啊,第一次登录失败可以记录,但如果出现前十分钟失败了4次,第11分钟又失败了一次的话,firstFailTime
应该往后取第二次失败登录的时间啊,我总不能手动定义100个变量吧。。。面试官看到估计脸都绿了。恨不得给我一个Mysql数据表,把每次登陆都给存下来,这样就可以很方便的查出某个时间区间登陆的情况。
不慌,咱们虽然不是大佬,但一点一点分析还是可以的,沉住气!等等,刚刚说到数据库存所有的登录数据??其实思考到上面已经快接近了,我不能手动创建100个变量,但我可以用一种数据结构依次记录登录失败的时间啊,突然想到LRU算法对不对!!能从数据顺序看出来时间顺序的数据结构不就是链表吗!!!还有登录验证的问题,不如偷个懒,用一个boolean控制。解决,cool~
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);
}
}
}
最后
以上就是轻松野狼为你收集整理的阿里面试官问我:如何设计登录接口,十分钟内连续登录5次失败,需要等待30分钟才能登录的全部内容,希望文章能够帮你解决阿里面试官问我:如何设计登录接口,十分钟内连续登录5次失败,需要等待30分钟才能登录所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复