之前讲了Shiro Security如何结合验证码,这次讲讲如何限制用户登录尝试次数,防止多次尝试,暴力破解密码情况出现。要限制用户登录尝试次数,必然要对用户名密码验证失败做记录,Shiro中用户名密码的验证交给了CredentialsMatcher
所以在CredentialsMatcher里面检查,记录登录次数是最简单的做法。Shiro天生和Ehcache是一对好搭档,无论是单机还是集群,都可以在Ehcache中存储登录尝试次数信息。
现在介绍一个简单的登录次数验证做法,实现一个RetryLimitCredentialsMatchers继承至HashedCredentialsMatcher,加入缓存,在每次验证用户名密码之前先验证用户名尝试次数,如果超过5次就抛出尝试过多异常,否则验证用户名密码,验证成功把尝试次数清零,不成功则直接退出。这里依靠Ehcache自带的timeToIdleSeconds来保证锁定时间(帐号锁定之后的最后一次尝试间隔timeToIdleSeconds秒之后自动清除)。
public class RetryLimitCredentialsMatcher extends HashedCredentialsMatcher {
//集群中可能会导致出现验证多过5次的现象,因为AtomicInteger只能保证单节点并发
private Cache<String, AtomicInteger> passwordRetryCache;
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
String username = (String)token.getPrincipal();
//retry count + 1
AtomicInteger retryCount = passwordRetryCache.get(username);
if(null == retryCount) {
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
if(retryCount.incrementAndGet() > 5) {
logger.warn("username: " + username + " tried to login more than 5 times in period");
throw new ExcessiveAttemptsException("username: " + username + " tried to login more than 5 times in period"
); }
boolean matches = super.doCredentialsMatch(token, info);
if(matches) {
//clear retry data
passwordRetryCache.remove(username);
}
return matches;
}
}
- Spring配置CacheManager
<bean id="springCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManager"/>
</bean>
<!--ehcache-->
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache/ehcache.xml"/>
</bean>
- Ehcache配置
<ehcache name="es">
<diskStore path="java.io.tmpdir"/>
<!-- 登录记录缓存 锁定100分钟 -->
<cache name="passwordRetryCache"
maxEntriesLocalHeap="20000"
eternal="false"
timeToIdleSeconds="36000"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="false">
</cache>
</ehcache>
- Spring Shiro配置
UserRealm继承AuthorizingRealm,在其父类AuthenticatingRealm的getAuthenticationInfo方法中会调用credentialsMatcher的
doCredentialsMatch
来验证用户输入用户名密码是否匹配。
<bean id="credentialsMatcher" class="com.cloud.service.security.credentials.RetryLimitCredentialsMatcher">
<constructor-arg ref="springCacheManager"/>
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>
<bean id="myRealm" class="com.cloud.service.security.UserRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
<property name="cachingEnabled" value="false"/>
<!--<property name="authenticationCachingEnabled" value="true"/>-->
<!--<property name="authenticationCacheName" value="authenticationCache"/>-->
<!--<property name="authorizationCachingEnabled" value="true"/>-->
<!--<property name="authorizationCacheName" value="authorizationCache"/>-->
</bean>
时间: 2024-07-29 18:11:39