分享

Shiro security限制登录尝试次数

 飞鹰飞龙飞天 2015-06-08

之前讲了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; 
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  1. 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  1. 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  1. 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多