springboot整合shiro登錄失敗次數(shù)限制功能的實(shí)現(xiàn)代碼
這次講講如何限制用戶登錄嘗試次數(shù),防止壞人多次嘗試,惡意暴力破解密碼的情況出現(xiàn),要限制用戶登錄嘗試次數(shù),必然要對用戶名密碼驗(yàn)證失敗做記錄,Shiro中用戶名密碼的驗(yàn)證交給了CredentialsMatcher 所以在CredentialsMatcher里面檢查,記錄登錄次數(shù)是最簡單的做法。當(dāng)?shù)卿浭〈螖?shù)達(dá)到限制,修改數(shù)據(jù)庫中的狀態(tài)字段,并返回前臺錯誤信息。
因?yàn)橹暗牟┛投际怯玫拿魑?這里就不對密碼進(jìn)行加密了,如果有需要加密,將自定義密碼比較器從SimpleCredentialsMatcher改為HashedCredentialsMatcher 然后將對應(yīng)的配置項(xiàng)打開就可以。
說在前面
非常抱歉,因?yàn)槲抑罢系臅r候,只是注意功能,而沒有注意細(xì)節(jié),導(dǎo)致在登錄失敗之后,再次轉(zhuǎn)發(fā)到 post方法/login 也就是真正的登錄方法,導(dǎo)致 再次登錄,然后導(dǎo)致下面密碼錯誤3次之后 就 鎖定 我設(shè)置的是5次.
所以將shiroConfig中的值改為shiroFilterFactoryBean.setLoginUrl("/");具體參考源代碼。
另外 還需要將 自定義ShiroRealm 中 密碼對比注銷掉, 將密碼對比 交給 底層的 密碼比較器才可以 鎖定用戶,否則將 永遠(yuǎn)報(bào)密碼錯誤。,具體代碼 如下:
修改登錄方法改為登錄之后,重定向到/index

限制登錄次數(shù)
自定義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: 登陸次數(shù)限制
*/
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();
//獲取用戶登錄次數(shù)
AtomicInteger retryCount = passwordRetryCache.get(username);
if (retryCount == null) {
//如果用戶沒有登陸過,登陸次數(shù)加1 并放入緩存
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
if (retryCount.incrementAndGet() > 5) {
//如果用戶登陸失敗次數(shù)大于5次 拋出鎖定用戶異常 并修改數(shù)據(jù)庫字段
User user = userMapper.findByUserName(username);
if (user != null && "0".equals(user.getState())){
//數(shù)據(jù)庫字段 默認(rèn)為 0 就是正常狀態(tài) 所以 要改為1
//修改數(shù)據(jù)庫的狀態(tài)字段為鎖定
user.setState("1");
userMapper.update(user);
}
logger.info("鎖定用戶" + user.getUsername());
//拋出用戶鎖定異常
throw new LockedAccountException();
}
//判斷用戶賬號和密碼是否正確
boolean matches = super.doCredentialsMatch(token, info);
if (matches) {
//如果正確,從緩存中將用戶登錄計(jì)數(shù) 清除
passwordRetryCache.remove(username);
}
return matches;
}
/**
* 根據(jù)用戶名 解鎖用戶
* @param username
* @return
*/
public void unlockAccount(String username){
User user = userMapper.findByUserName(username);
if (user != null){
//修改數(shù)據(jù)庫的狀態(tài)字段為鎖定
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");
//配置加密的次數(shù)
//retryLimitHashedCredentialsMatcher.setHashIterations(1024);
//是否存儲為16進(jìn)制
//retryLimitHashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return retryLimitHashedCredentialsMatcher;
}
在shiroRealm中配置密碼比較器
/**
* 身份認(rèn)證realm; (這個需要自己寫,賬號密碼校驗(yàn);權(quán)限等)
* @return
*/
@Bean
public ShiroRealm shiroRealm(){
ShiroRealm shiroRealm = new ShiroRealm();
......
//配置自定義密碼比較器
shiroRealm.setCredentialsMatcher(retryLimitHashedCredentialsMatcher());
return shiroRealm;
}
在ehcache-shiro.xml添加緩存項(xiàng)
<!-- 登錄失敗次數(shù)緩存 注意 timeToLiveSeconds 設(shè)置為300秒 也就是5分鐘 可以根據(jù)自己的需求更改 --> <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 權(quán)限改為 任何人可訪問。
在login.html頁面 添加 解鎖admin用戶的按鈕
<a href="/unlockAccount" rel="external nofollow" >解鎖admin用戶</a></button>
測試結(jié)果

總結(jié)
以上所述是小編給大家介紹的springboot整合shiro-登錄失敗次數(shù)限制,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
SpringBoot集成SpringSecurity安全框架方式
這篇文章主要介紹了SpringBoot集成SpringSecurity安全框架方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05
Java通過XPath獲取XML文件中符合特定條件的節(jié)點(diǎn)
今天小編就為大家分享一篇關(guān)于Java通過XPath獲取XML文件中符合特定條件的節(jié)點(diǎn),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01
使用Java根據(jù)文件路徑下載zip文件到本地代碼示例
在開發(fā)過程中我們會遇到需要對文件進(jìn)行壓縮并下載的功能需求,這篇文章主要給大家介紹了關(guān)于如何使用Java根據(jù)文件路徑下載zip文件到本地的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03
Java ProcessBuilder執(zhí)行多次CMD命令的使用
本文介紹了Java的ProcessBuilder類,該類用于執(zhí)行外部命令,通過ProcessBuilder,我們可以在Java程序中靈活地執(zhí)行多次CMD命令,并控制輸入輸出流以及工作目錄等,感興趣的可以了解一下2024-11-11
淺談maven的jar包和war包區(qū)別 以及打包方法
下面小編就為大家分享一篇淺談maven的jar包和war包區(qū)別 以及打包方法,具有很好的參考價(jià)值,希望對大家有所幫助2017-11-11
Spring?Boot整合Bootstrap的超詳細(xì)步驟
之前做前端開發(fā),在使用bootstrap的時候都是去官網(wǎng)下載,然后放到項(xiàng)目中,在頁面引用,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot整合Bootstrap的超詳細(xì)步驟,需要的朋友可以參考下2023-05-05
MyBatis創(chuàng)建存儲過程的實(shí)例代碼_動力節(jié)點(diǎn)Java學(xué)院整理
本節(jié)需要用到的有2部分,第一部分是如何在Derby中創(chuàng)建存儲過程,第二部分是如何在Mybatis中調(diào)用存儲過程,具體實(shí)例代碼大家參考下本文吧2017-09-09

