Shiro安全框架學習04 - 登錄失敗次數限制

爲了防止被惡意暴力破解,我們都會進行登錄失敗超過一定次數進行鎖定賬號禁止登錄。使用Ehcache提供緩存服務。

在前幾篇代碼的基礎上添加ehcache依賴
<dependency>
	<groupId>net.sf.ehcache</groupId>
	<artifactId>ehcache-core</artifactId>
	<version>2.6.6</version>
</dependency>
配置ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es">

    <diskStore path="java.io.tmpdir"/>

    <!-- 登錄記錄緩存 鎖定10分鐘 -->
    <cache name="passwordRetryCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

</ehcache>
編寫自定義的CredentialsMatcher
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import java.util.concurrent.atomic.AtomicInteger;

public class RetryLimitHashedCredentialsMatcher extends
		HashedCredentialsMatcher {

	private Ehcache passwordRetryCache;

    public RetryLimitHashedCredentialsMatcher() {
        CacheManager cacheManager = CacheManager.newInstance(CacheManager.class.getClassLoader().getResource("ehcache.xml"));
        passwordRetryCache = cacheManager.getCache("passwordRetryCache");
    }

	@Override
	public boolean doCredentialsMatch(AuthenticationToken token,
			AuthenticationInfo info) {
		String username = (String)token.getPrincipal();
        //retry count + 1
        Element element = passwordRetryCache.get(username);
        if(element == null) {
            element = new Element(username , new AtomicInteger(0));
            passwordRetryCache.put(element);
        }
        AtomicInteger retryCount = (AtomicInteger)element.getObjectValue();
        if(retryCount.incrementAndGet() > 5) {
            //if retry count > 5 throw
            throw new ExcessiveAttemptsException();
        }

        boolean matches = super.doCredentialsMatch(token, info);
        if(matches) {
            //clear retry count
            passwordRetryCache.remove(username);
        }
        return matches;
	}
}
編寫自定義的Realm
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;


public class UserRealm extends AuthorizingRealm  {

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		// TODO Auto-generated method stub
		
		//獲取賬號密碼
		UsernamePasswordToken t = (UsernamePasswordToken) token;
        String password = new String(t.getPassword());
		String username = (String) token.getPrincipal();
		
		//獲取數據庫種的密碼、鹽(此處寫死,測試用)
		User user = new User();
		user.setUsername(username);
		user.setPassword("b4b88b2ef28089f9c802df11bd216a33");
		user.setSalt("713183f112f48824613f0f05d0d10060");
		
		if (!"itmyhome".equals(username)) {
			// 拋出 帳號找不到異常
			throw new UnknownAccountException();
		}
		
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
				user.getUsername(), // 用戶名
				user.getPassword(), // 密碼
				ByteSource.Util.bytes(password + user.getSalt()),// salt=password+salt
				getName() // realm name
		);
		return authenticationInfo;
	}

}
測試類
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ShiroTest {

	private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);

	public static void main(String[] args) {
      
		//獲取SecurityManager工廠,此處使用Ini配置文件初始化SecurityManager  
        Factory<SecurityManager> factory =  new IniSecurityManagerFactory("classpath:auth.ini");
 
        //得到SecurityManager實例 並綁定給SecurityUtils  
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        
        //得到Subject及創建用戶名/密碼身份驗證Token(即用戶身份/憑證)  
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("itmyhome","1234561");
        
        //模擬多次登錄,密碼錯誤超過5次即鎖定賬戶
    	for (int i = 1; i <= 6; i++){ 
	        try{
	        	//登錄,即身份驗證  
	        	subject.login(token);
	        }catch(ExcessiveAttemptsException e){
	        	System.out.println("###### 賬戶已鎖定 ########");
	        }catch(IncorrectCredentialsException e){
	        	System.out.println("###### 密碼錯誤 ########");
	        }catch(AuthenticationException e){
	        	//身份驗證失敗
	        	System.out.println("###### 賬號錯誤 ########");
	        }
    	}
        System.out.println("User is authenticated:  " + subject.isAuthenticated());
    }
}

示例源碼地址:xxx

發佈了691 篇原創文章 · 獲贊 2948 · 訪問量 409萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章