主要就是用來限制用戶登錄嘗試次數的,登陸失不失敗,與密碼認證有關,所以要自定義一個密碼匹配器,繼承原來的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-登錄失敗次數限制(八)