Shiro功能應用(六)--登陸失敗重試次數控制

     主要就是用來限制用戶登錄嘗試次數的,登陸失不失敗,與密碼認證有關,所以要自定義一個密碼匹配器,繼承原來的HashedCredentialsMatcher密碼匹配器,重寫驗證方法doCredentialsMatch。本文在上一篇文章Shiro功能應用(五)–Session管理的登陸人數控制代碼基礎進行添加登陸次數限制。

代碼實現:

      代碼地址:
          https://github.com/OooooOz/SpringBoot-Shiro

     ShiroConfig的自定義Realm注入自定義密碼比較器:

 @Bean(name="shiroRealm")
    public MyShiroRealm getMyShiroRealm(@Qualifier("credentialsMatcherLimit")HashedCredentialsMatcher credentialsMatcher){
        MyShiroRealm shiroRealm = new MyShiroRealm();
        //shiroRealm.setCredentialsMatcher(credentialsMatcher);//設置密碼比較器,beanId=credentialsMatcher
        shiroRealm.setCredentialsMatcher(credentialsMatcher);//設置自定義密碼比較器,beanId=credentialsMatcherLimit
       ... ... ... ... 
        return shiroRealm;
    }

     ShiroConfig的自定義密碼比較器:

    @Bean("credentialsMatcherLimit")
    public RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher(){
        RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(getEhCacheManager());
        retryLimitHashedCredentialsMatcher.setHashAlgorithmName("MD5");	//加密算法的名稱
        retryLimitHashedCredentialsMatcher.setHashIterations(1);		//配置加密的次數
        retryLimitHashedCredentialsMatcher.setRetryLimitNum(3);		//配置加密的次數
        //retryLimitHashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);//是否存儲爲16進制
        return retryLimitHashedCredentialsMatcher;
    }

     自定義密碼比較器類:

package com.demo.config;

import java.util.concurrent.atomic.AtomicInteger;

import com.demo.dao.IUserMapper;
import com.demo.entity.User;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;


/**
 * @description: 登陸次數限制
 */
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {

    @Autowired
    private IUserMapper userMapper;
    private Cache<String, AtomicInteger> 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) {
            //如果用戶沒有登陸過,登陸次數加1 並放入緩存
            retryCount = new AtomicInteger(0);
            passwordRetryCache.put(username, retryCount);
        }
        if (retryCount.incrementAndGet() >retryLimitNum) {
            //如果用戶登陸失敗次數大於retryLimitNum, 拋出鎖定用戶異常  並修改數據庫字段
            User user = userMapper.findByUserName(username);
            if (user != null && 0==user.getStatus()){
                //數據庫字段 默認爲 0  就是正常狀態 所以 要改爲1
                //修改數據庫的狀態字段爲鎖定
                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);
        }
    }

}

     EHCache緩存配置文件:

	<!-- 登錄失敗次數緩存
	     注意 timeToLiveSeconds 設置爲300秒 也就是5分鐘
	     可以根據自己的需求更改
	 -->
	<cache name="passwordRetryCache"
	       maxEntriesLocalHeap="2000"
	       eternal="false"
	       timeToIdleSeconds="0"
	       timeToLiveSeconds="300"
	       overflowToDisk="false"
	       statistics="true">
	</cache>

執行過程:

     在Shiro內部認證方法中,執行doCredentialsMatch會跳轉到自定義密碼匹配器的方法。使用了EHCache(構造方法注入的new RetryLimitHashedCredentialsMatcher(getEhCacheManager());)
     第一次登陸,緩存無數據,retryCount=null,初始化次數=0,然後將次數put進緩存,在調用父類的super.doCredentialsMatch(token, info); 時,如果通過認證,則將之前放入的緩存移除調,如果認證失敗,則retryCount會自動加1,(retryCount爲AtomicInteger類型),此時爲第一次登陸失敗。
     如果登陸次數大於指定次數,則將用戶狀態更新,然後拋出用戶鎖定異常。緩存設置類300s失效,所以300s內是無法登陸的,300s後才能登陸。
     在控制器中捕獲異常,輸出異常信息到前臺
在這裏插入圖片描述
在這裏插入圖片描述
     參考文章
     springboot整合shiro-登錄失敗次數限制(八)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章