2022年1月7日更新:
既然大家的讨论热情这么高,我觉得就有必要把这篇文章再好好完善一下,评论区里大家给出的比较一致的实现方式是使用redis,我就把实现的思路也加进来吧。
part 1 面试白板编程实现(原生JDK)
常言道:字数越短问题越大。
今天阿里的面试官小哥哥让我实现一个登录接口,同一个用户10分钟内连续登陆5次失败,则需要等到30分钟才能登陆。
当然大佬估计一看到这种题目会很难过,一丁点算法都没有,妙解没意思。我上来就被唬住了。登录接口?10分钟内连续5次??等待30分钟才能登陆???登陆验证????
问号一下子就冒出来了,当然最开始我想定义一个变量firstFailTime
来记录第一次失败的时间,再仔细一想不对啊,firstFailTime
是动态的额,要不断变化,单一个变量不好实现啊,第一次登录失败可以记录,但如果出现前十分钟失败了4次,第11分钟又失败了一次的话,firstFailTime
应该往后取第二次失败登录的时间啊,我总不能手动定义100个变量吧。。。面试官看到估计脸都绿了。恨不得给我一个Mysql数据表,把每次登陆都给存下来,这样就可以很方便的查出某个时间区间登陆的情况。
不慌,咱们虽然不是大佬,但一点一点分析还是可以的,沉住气!等等,刚刚说到数据库存所有的登录数据??其实思考到上面已经快接近了,我不能手动创建100个变量,但我可以用一种数据结构依次记录登录失败的时间啊,突然想到LRU算法对不对!!能从数据顺序看出来时间顺序的数据结构不就是链表吗!!!还有登录验证的问题,不如偷个懒,用一个boolean控制。解决,cool~
P.S:我没考虑开多个线程去测试,因为我个人感觉用户登录不会出现在高并发的环境里,几万个人同时登陆同一个账号想想就离谱……但为了保险起见我还是给map加了synchronize关键字。
Person类:
1 | java复制代码package exam; |
主类:
1 | java复制代码package exam; |
part 2 使用缓存来实现
实现步骤:
- 用户发起登录请求
- 后台验证是否失败次数过多,账户没有锁定的话就进入下面的步骤;否则直接返回
- 验证用户的账号 + 密码
3.1 验证成功:删除缓存
3.2 验证失败:统计最近10分钟时间窗口内的失败次数,如果达到5次则设置锁定缓存,返回
图解实现步骤:
代码实现细节:
- 登录失败计数器的key设计为:一串字符串 + 用户名(假设具有唯一性)+ 登录失败的时间
- 锁定登录操作的key设计为:一串字符串 + 用户名(假设具有唯一性)
1 | java复制代码private static final String FAIL_COUNT_REDIS_KEY = "login_fail_count"; |
用户登录服务:
1 | java复制代码@Override |
登陆失败的话,就给登录失败次数加一:
1 | java复制代码@Override |
如果登录成功,则删除失败次数计数器:
1 | java复制代码@Override |
失败次数超过5次则禁止登录,只需要设置一个缓存即可:
1 | java复制代码@Override |
判断是否被禁止登录,只需要判断是否有上面方法设置的key即可:
1 | java复制代码@Override |
如果想要获取到用户具体需要几分钟才能解锁(用于提示信息),只需要查询缓存的过期时间:
1 | java复制代码private Long unLockTime(String username){ |
本文转载自: 掘金