很常见的一个场景:用户使用账户名密码登录的时候,如果输错了密码,会被系统记录错误的次数,当一定时间内,密码错误次数超过N次就会锁定账户一段时间。在这段时间内,该账户不能使用密码的方式进行登录。
目的是为了防止暴力破解密码,恶意请求等 。保护账户安全。
需求
用户在10分钟内,最多只能输错5次密码,否则锁定账户30分钟,在此期间不能通过密码的方式进行登录。
实现思路
账户的锁定,基于Redis的过期key实现。使用用户的账户作为Redis的key,登录先检查,Redis中是否有这个key的存在。如果存在,则表示账户已经被锁定 。不能使用密码的方式进行登录。这种锁定一般都是临时性的,所以这个Key是有过期时间的。
banned:{account}: 1
登录错误时,错误次数的统计,也是基于Redis的过期key实现,在Redis中以用户的账户作为key,错误次数作为value存储。在第1次设置的时候,设置它的过期时间。
failed:{account}: {count}
实现代码
import java.time.Duration;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/login")
public class LoginController {
public static final String BANNED_KEY = "banned:";
public static final String FAILED_KEY = "failed:";
@Autowired
private StringRedisTemplate stringRedisTemplate;
@PostMapping
public Object login(@RequestParam("account") String account,
@RequestParam("password") String password) {
// 判断账户是否被锁定了
String bannedKey = BANNED_KEY + account;
if(this.stringRedisTemplate.hasKey(bannedKey)) {
return Map.ofEntries(Map.entry("success", false), Map.entry("message", "错误次数太多,稍后再试或者选择其他的登录方式"));
}
// 登录成功
if (account.equals("admin") && password.equals("123456")) { // 假设了正确的账户名密码
return Map.ofEntries(Map.entry("success", true));
}
// 记录登录失败的次数,并且判断是否要临时锁定账户
String failedKey = FAILED_KEY + account;
long count = this.stringRedisTemplate.opsForValue().increment(failedKey);
if (count == 1) {
// 第1次失败,设置key的过期时间,10分钟。
this.stringRedisTemplate.expire(failedKey, Duration.ofMinutes(10));
} else if(count >= 5){
// 10分钟内,密码错误超过5次,锁定账户30分钟。
this.stringRedisTemplate.opsForValue().set(bannedKey, "", Duration.ofMinutes(30));
}
return Map.ofEntries(Map.entry("success", false), Map.entry("message", "用户名密码错误"));
}
}
实现的逻辑和代码都极其简单,测试结果也正常。节约篇幅,这里就不贴了。
最后
现在的应用,大都提供了不止一种的登录方式,Oatuh2,短信,扫码等等。禁止用户使用账户名密码登录后,应该让用户可以选择其他有效的方式进行登录。