1. 程式人生 > >springboot整合shiro-登入失敗次數限制(八)

springboot整合shiro-登入失敗次數限制(八)

這次講講如何限制使用者登入嘗試次數,防止壞人多次嘗試,惡意暴力破解密碼的情況出現,要限制使用者登入嘗試次數,必然要對使用者名稱密碼驗證失敗做記錄,Shiro中使用者名稱密碼的驗證交給了CredentialsMatcher 所以在CredentialsMatcher裡面檢查,記錄登入次數是最簡單的做法。當登入失敗次數達到限制,修改資料庫中的狀態欄位,並返回前臺錯誤資訊。
因為之前的部落格都是用的明文,這裡就不對密碼進行加密了,如果有需要加密,將自定義密碼比較器從SimpleCredentialsMatcher改為HashedCredentialsMatcher 然後將對應的配置項開啟就可以。

說在前面

非常抱歉,因為我之前整合的時候,只是注意功能,而沒有注意細節,導致在登入失敗之後,再次轉發到 post方法/login 也就是真正的登入方法,導致 再次登入,然後導致下面密碼錯誤3次之後 就 鎖定 我設定的是5次.
所以將shiroConfig中的值改為shiroFilterFactoryBean.setLoginUrl("/");具體參考原始碼。
另外 還需要將 自定義ShiroRealm 中 密碼對比登出掉, 將密碼對比 交給 底層的 密碼比較器才可以 鎖定使用者,否則將 永遠報密碼錯誤。,具體程式碼 如下:
這裡寫圖片描述
修改登入方法改為登入之後,重定向到/index
這裡寫圖片描述

限制登入次數

自定義RetryLimitHashedCredentialsMatcher繼承SimpleCredentialsMatcher
package com.springboot.test.shiro.config.shiro;

import java.util.concurrent.atomic.AtomicInteger;

import com.springboot.test.shiro.modules.user.dao.UserMapper;
import com.springboot.test.shiro.modules.user.dao.entity.User;
import
org.apache.log4j.Logger; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.LockedAccountException; 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; /** * @author: WangSaiChao * @date: 2018/5/25 * @description: 登陸次數限制 */ public class RetryLimitHashedCredentialsMatcher extends SimpleCredentialsMatcher { private static final Logger logger = Logger.getLogger(RetryLimitHashedCredentialsMatcher.class); @Autowired private UserMapper userMapper; 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(); //獲取使用者登入次數 AtomicInteger retryCount = passwordRetryCache.get(username); if (retryCount == null) { //如果使用者沒有登陸過,登陸次數加1 並放入快取 retryCount = new AtomicInteger(0); passwordRetryCache.put(username, retryCount); } if (retryCount.incrementAndGet() > 5) { //如果使用者登陸失敗次數大於5次 丟擲鎖定使用者異常 並修改資料庫欄位 User user = userMapper.findByUserName(username); if (user != null && "0".equals(user.getState())){ //資料庫欄位 預設為 0 就是正常狀態 所以 要改為1 //修改資料庫的狀態欄位為鎖定 user.setState("1"); userMapper.update(user); } logger.info("鎖定使用者" + user.getUsername()); //丟擲使用者鎖定異常 throw new LockedAccountException(); } //判斷使用者賬號和密碼是否正確 boolean matches = super.doCredentialsMatch(token, info); if (matches) { //如果正確,從快取中將使用者登入計數 清除 passwordRetryCache.remove(username); } return matches; } /** * 根據使用者名稱 解鎖使用者 * @param username * @return */ public void unlockAccount(String username){ User user = userMapper.findByUserName(username); if (user != null){ //修改資料庫的狀態欄位為鎖定 user.setState("0"); userMapper.update(user); passwordRetryCache.remove(username); } } }
在shiroConfig中配置該bean
/**
 * 配置密碼比較器
 * @return
 */
@Bean("credentialsMatcher")
public RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher(){
    RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(ehCacheManager());

    //如果密碼加密,可以開啟下面配置
    //加密演算法的名稱
    //retryLimitHashedCredentialsMatcher.setHashAlgorithmName("MD5");
    //配置加密的次數
    //retryLimitHashedCredentialsMatcher.setHashIterations(1024);
    //是否儲存為16進位制
    //retryLimitHashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);

    return retryLimitHashedCredentialsMatcher;
}
在shiroRealm中配置密碼比較器
/**
 *  身份認證realm; (這個需要自己寫,賬號密碼校驗;許可權等)
 * @return
 */
@Bean
public ShiroRealm shiroRealm(){
    ShiroRealm shiroRealm = new ShiroRealm();

    ......

    //配置自定義密碼比較器
    shiroRealm.setCredentialsMatcher(retryLimitHashedCredentialsMatcher());
    return shiroRealm;
}
在ehcache-shiro.xml新增快取項
<!-- 登入失敗次數快取
     注意 timeToLiveSeconds 設定為300秒 也就是5分鐘
     可以根據自己的需求更改
 -->
<cache name="passwordRetryCache"
       maxEntriesLocalHeap="2000"
       eternal="false"
       timeToIdleSeconds="0"
       timeToLiveSeconds="300"
       overflowToDisk="false"
       statistics="true">
</cache>
在LoginController中新增解除admin使用者限制方法
/**
 * 解除admin 使用者的限制登入 
 * 寫死的 方便測試
 * @return
 */
@RequestMapping("/unlockAccount")
public String unlockAccount(Model model){
    model.addAttribute("msg","使用者解鎖成功");

    retryLimitHashedCredentialsMatcher.unlockAccount("admin");

    return "login";
}

注意:為了方便測試,記得將 unlockAccount 許可權改為 任何人可訪問。

在login.html頁面 新增 解鎖admin使用者的按鈕
<a href="/unlockAccount">解鎖admin使用者</a></button>
測試結果

這裡寫圖片描述