具体场景和操作是自行实现,基础代码参考自: SpringBoot注册登录(四):登录功能–密码错误三次,需要等待2分钟才能登录,固定时间内不能登录.
介绍
第一次写博客,想分享下学到的知识和自己的想法,希望能够帮助同样受到困惑的朋友,同时也是给容易失忆的自己留个mark,由于我容易忘所以我喜欢把这些东西写得稍微详细点.如果有什么错误希望大家帮忙指出,本人语言组织有点差,希望多多谅解.这个知识也是我站在巨人的肩膀上前进,所以非常感谢帮助我的博主.
问题
在springboot框架下的登录界面,如何实现把重复登录失败的用户进行锁定,防止有人通过无限试错来刷出密码.
对问题的想法
首先,我想到的是对数据库的表增加字段,用于查询判断这个用户的情况,根据返回的结果做出不同的判断,是锁住用户还是常规用户.
具体操作
1.首先,根据需求,我想对用户表(假定是user表)进行新添加字段:第一个是登录失败次数(为int类型默认为0,不准为空).第二个是允许登录的时间(datetime类型,允许为空)[这个两个字段就足够对这个问题解决,但后来我发现会存在一个小问题,按照之后的逻辑会对三次登录失败的用户锁三分钟,假设我有个用户,先猴急登录了2次,全都失败了,有点慌,怕被锁住,思考了三分钟,想出个几个密码,先去试了下第一个,还是出错了,那就会进入到三分钟锁定阶段.这个用户不服,我都思考了三分钟,你还要我等三分钟.如果考虑这种情况,我们想人性化点三分钟后清除,可以再加个字段上次测试的时间,但我当时处理的时候没考虑,就算了]
// 数据库里面负责添加字段的代码
ALTER table user
ADD times int NOT NULL DEFAULT 0 ;
ALTER table user
ADD allow_time DATETiME ;
2.在对应的项目文件domain的User项目中把新加入的字段和方法写入(User类里面写表相应的字段和方法,就是持久层的处理,假设我其他的字段都已经写好了,方式为通过注解进行处理)
// 持久层的数据
private Long times;
private Date allow_time;
@Column(name = "times")//这里对应的是数据库字段名
public Long getTimes() {
return times;
}
public void setTimes(Long times) {
this.times = times;
}
@Colume(name = "allow_time")
public Date getAllow_time() {
return allow_time;
}
public void setAllow_time(Date allow_time) {
this.allow_time = allow_time;
}
3.接下来操作对应的Controller项目,假设我已经有一个login的方法了,他在UserController类,url是 巴拉巴拉+/login.他已经实现了登录的其他操作.此时的修改思路为,在判断为密码失败的后面假如操作(失败了肯定是次数加一啊,如果用户名不存在或者出现其他的异常我们就不管了,毕竟你用户名都没有,怎么可能给你试出来,但如果一定要加也是可以的)此处,添加一个我们的方法lockedJudge,用于判断是否被锁住了.如果返回false代表被锁住了,返回true代表没被锁住,需要获取的参数是id(假设有个用于辨识用户的字段叫id)失败次数和允许时间(这两个参数是得到的参数,如果次数大于3后修改的数据会不一样,可能会埋下小伏笔),以及判断是否登录成功(如果被锁住的状态下让他测试出正确密码,可不能让他进去,否则我们一切都白费了,这个方法之后成功的部分也要有),就是我们增加的字段.操作为:
如果允许时间为空或者当前时间大于允许时间{
是:查询是否失败次数在三次以上[
是:三次以上的错误,把次数变成0,允许时间推迟到3分后,并返回false.代表锁住了.
不是:根据传入的第三个参数是否登录成功进行判断,密码错误进来说明现在没在锁住的阶段,但你密码错了,总要给我次数加一吧.成功进来就啥也不做,他们都是返回true.因为我们给的结果是账户是不是被锁住了.并不是密码是否正确(这里当初我自己把自己给绕了)
]
不是:说明没到解锁的时间,直接返回false代表锁住了
}
//我的判断是否锁住的代码
private boolean lockedJudge(int id, Long times, Date allow_time, boolean loginSuccess) {
Date dateNow = new Date();
if (allow_time == null || dateNow.getTime() > allow_time.getTime()) {
if (times >= 3) {
//等会要在Service层加把次数清零和时间推后三分钟的方法
userService.TimeClear(id);
Date dateAfterAllowTime = new Date(dateNow.getTime() + 18000);
userService.pushAllowedTime(id, dateAfterAllowTime);
System.out.println("三次密码错误,三分钟后再尝试");//假装是个报错信息
return false;
} else {
if (loginSuccess) {
//登录成功了,次数是要变回0;
userService.TimeClear(id);
System.out.println("登录成功");
return true;
} else {
//写个错误次数加一的方法
userService.allowedTimeAdd(id);
System.out.println("密码错误");
return true;
}
}
} else {
System.out.println("未到解锁时间");
return false;
}
}
之后在成功的部分也加上这个方法,毕竟账户被锁住的优先级比较高嘛.接下来就讲下Service那边就把上面这三个方法写个接口,再到Impl里写给Rep类(假设我有个userRep已经写好了)加三个方法(如果出现报错说只有读取权限,没有修改权限之类的,就在方法前面加注解@Transactional(readOnly=false)),他的作用就是可以直接通过sql语句操作数据库,
@Modifying
@Query(value = "update User set times=0 where id=?1 ")
void TimeClear(int id);
@Modifying
@Query(value = "update User set allow_time=?2 where id=?1 ")
void pushAllowedTime(int id, Date dateAfterAllowTime);
@Modifying
@Query(value = "update User set times=times+1 where id=?1 ")
void pushAllowedTime(int id);
当然还可以再人性化点,可以再写个显示还有多少秒后账户解锁的方法,代码是写在成功和失败方法里面判断是否锁住的后面,此时就要根据我们方法的结果进行判断,被锁住了就再获取下当前的时间和修改后的允许登录时间(对上上面的小伏笔,注意这里要重新获取,否则只会取到没修改之前的数据),然后进行显示