1 引言
系统为了避免密码遭到暴力破解,通常情况下需要在登录时,限制用户验证账号密码的次数,当达到一定的验证次数后,在一段时间内锁定该账号,不再验证。本章将用几行代码实现该功能,完整代码链接在文章最后。
2 原理介绍
可以看到在登录接口中,4行代码即可实现该功能,这里使用Redis可以很方便的记录"登录失败次数",以及设置其失效时间(即锁定时间),主要步骤是:
- 账号登录时,当前账号"登录失败次数"默认+1。
- 设置"登录失败次数"失效的时间(即锁定时间)。
- 当"登录失败次数"大于某个阈值时,就直接返回错误提示,不再走验证逻辑。
- 账号登录成功,清除"登录失败次数",重新计数。
注意:第2步和第3步,可以交换顺序。
第2步在前时,锁定中发起请求会导致重新计时。
第3步在前时,锁定中发起请求不会导致重新计时。
3 代码
isTrue是断言方法,不满足条件时,抛出异常,在全局异常处理中,统一返回错误提示。参见Spring全局异常处理HandlerExceptionResolver使用。
package com.zeroone.service.sys;
import com.zeroone.common.RedisKey;
import com.zeroone.entity.sys.User;
import com.zeroone.service.BaseService;
import com.zeroone.utils.Param;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service("UserService")
public class LoginServiceImpl extends BaseService implements LoginService {
@Override
public Object webLogin(Param info) {
String account = info.getStringNotNull("account").trim();
String password = info.getStringNotNull("password").trim();
Long loginFailedCount = redisTemplate.opsForValue().increment(RedisKey.LOGIN_FAILED_COUNT + account, 1);//先将登录失败次数+1
redisTemplate.expire(RedisKey.LOGIN_FAILED_COUNT + account, 10, TimeUnit.SECONDS);// 指定登录失败次数失效时间
isTrue(loginFailedCount <= 5, "请10秒后重试!");//当登录失败次数大于阈值后,直接返回错误信息
//TODO 模拟验证密码
isTrue(password.equals("12345"), "账号或密码错误,还能重试" + (5 - loginFailedCount) + "次!");
redisTemplate.delete(RedisKey.LOGIN_FAILED_COUNT + account);// 验证通过后,清除登录失败次数
User user = new User();
user.setId(1L);
user.setName("张三");
user.setAccount("12345678901@qq.com");
user.setPhone("12345678901");
String token = tokenService.createToken(user);
return token;
}
@Override
public void logout() {
tokenService.deleteToken();
}
}
/**
* true断言
*
* @param expression 参数
* @param msg 描述
*/
public void isTrue(boolean expression, String msg) {
if (!expression) {
throw new MyRuntimeException(HttpCode.BAD_REQUEST_CODE, msg);
}
}
4 测试
- 然后连续验证错误5次后,提示"请10秒后重试!",表示该账号被锁定,拒绝验证,且10秒内无法再次请求验证。