本文旨在探讨如何在Apache Shiro框架中实现用户登录失败重试次数的限制,以提高系统的安全性。通过自定义密码匹配器,我们可以有效地防止恶意用户通过暴力破解的方式尝试登录系统。
背景介绍
在Web应用中,用户登录是一个常见的功能。然而,恶意用户可能会利用自动化工具频繁尝试不同的用户名和密码组合,以期找到正确的凭证。这种行为不仅消耗服务器资源,还可能危及系统安全。因此,限制用户登录失败的重试次数是一种有效的防御措施。
解决方案
为了实现这一功能,我们可以通过自定义密码匹配器来跟踪每个用户的登录尝试次数,并在达到预设的重试次数后锁定账户。具体来说,我们将继承Shiro的HashedCredentialsMatcher
类,并重写其doCredentialsMatch
方法。
代码实现
1. 自定义密码匹配器
首先,我们需要创建一个自定义的密码匹配器类,该类继承自HashedCredentialsMatcher
,并重写doCredentialsMatch
方法:
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
@Autowired
private IUserMapper userMapper;
private Cache passwordRetryCache;
private int retryLimitNum;
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
}
public void setRetryLimitNum(int retryLimitNum) {
this.retryLimitNum = retryLimitNum;
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
String username = (String) token.getPrincipal();
AtomicInteger retryCount = passwordRetryCache.get(username);
if (retryCount == null) {
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
if (retryCount.incrementAndGet() > retryLimitNum) {
User user = userMapper.findByUserName(username);
if (user != null && 0 == user.getStatus()) {
user.setStatus(1);
userMapper.update(user);
}
System.out.println("锁定用户" + user.getUserName());
throw new LockedAccountException();
}
boolean matches = super.doCredentialsMatch(token, info);
if (matches) {
passwordRetryCache.remove(username);
}
return matches;
}
public void unlockAccount(String username) {
User user = userMapper.findByUserName(username);
if (user != null) {
user.setStatus(0);
userMapper.update(user);
passwordRetryCache.remove(username);
}
}
}
2. 配置Shiro
接下来,在ShiroConfig
类中配置自定义的密码匹配器:
@Bean(name = "shiroRealm")
public MyShiroRealm getMyShiroRealm(@Qualifier("credentialsMatcherLimit") HashedCredentialsMatcher credentialsMatcher) {
MyShiroRealm shiroRealm = new MyShiroRealm();
shiroRealm.setCredentialsMatcher(credentialsMatcher);
return shiroRealm;
}
@Bean("credentialsMatcherLimit")
public RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher() {
RetryLimitHashedCredentialsMatcher matcher = new RetryLimitHashedCredentialsMatcher(getEhCacheManager());
matcher.setHashAlgorithmName("MD5");
matcher.setHashIterations(1);
matcher.setRetryLimitNum(3);
return matcher;
}
3. EHCache配置
最后,配置EHCache以存储登录失败次数的缓存:
执行过程
当用户尝试登录时,Shiro会调用自定义的doCredentialsMatch
方法。首次登录时,缓存中没有该用户的记录,因此初始化重试次数为0,并将其存入缓存。如果认证成功,则清除缓存中的记录;如果认证失败,则重试次数加1。当重试次数超过预设值时,系统将锁定用户账户,并抛出LockedAccountException
异常。
缓存设置为300秒(5分钟)后失效,因此用户在锁定后的5分钟内无法再次尝试登录。在控制器中捕获异常,并将异常信息返回给前端。
参考文章:
Spring Boot整合Shiro - 登录失败次数限制