日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

springboot + shiro 尝试登录次数限制与并发登录人数控制

發(fā)布時間:2025/3/21 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springboot + shiro 尝试登录次数限制与并发登录人数控制 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

源碼項目地址

嘗試登錄次數(shù)控制實現(xiàn)

實現(xiàn)原理

Realm在驗證用戶身份的時候,要進行密碼匹配。最簡單的情況就是明文直接匹配,然后就是加密匹配,這里的匹配工作則就是交給CredentialsMatcher來完成的。我們在這里繼承這個接口,自定義一個密碼匹配器,緩存入鍵值對用戶名以及匹配次數(shù),若通過密碼匹配,則刪除該鍵值對,若不匹配則匹配次數(shù)自增。超過給定的次數(shù)限制則拋出錯誤。這里緩存用的是ehcache。

shiro-ehcache配置

maven依賴

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.3.2</version> </dependency> 復制代碼

ehcache配置

<?xml version="1.0" encoding="UTF-8"?> <ehcache name="es"><diskStore path="java.io.tmpdir"/><!--name:緩存名稱。maxElementsInMemory:緩存最大數(shù)目maxElementsOnDisk:硬盤最大緩存?zhèn)€數(shù)。eternal:對象是否永久有效,一但設(shè)置了,timeout將不起作用。overflowToDisk:是否保存到磁盤,當系統(tǒng)當機時timeToIdleSeconds:設(shè)置對象在失效前的允許閑置時間(單位:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閑置時間無窮大。timeToLiveSeconds:設(shè)置對象在失效前允許存活時間(單位:秒)。最大時間介于創(chuàng)建時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。diskPersistent:是否緩存虛擬機重啟期數(shù)據(jù) Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskSpoolBufferSizeMB:這個參數(shù)設(shè)置DiskStore(磁盤緩存)的緩存區(qū)大小。默認是30MB。每個Cache都應該有自己的一個緩沖區(qū)。diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據(jù)指定的策略去清理內(nèi)存。默認策略是LRU(最近最少使用)。你可以設(shè)置為FIFO(先進先出)或是LFU(較少使用)。clearOnFlush:內(nèi)存數(shù)量最大時是否清除。memoryStoreEvictionPolicy:Ehcache的三種清空策略;FIFO,first in first out,這個是大家最熟的,先進先出。LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,緩存的元素有一個hit屬性,hit值最小的將會被清出緩存。LRU,Least Recently Used,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那么現(xiàn)有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。--><defaultCachemaxElementsInMemory="10000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"overflowToDisk="false"diskPersistent="false"diskExpiryThreadIntervalSeconds="120"/><!-- 登錄記錄緩存鎖定10分鐘 --><cache name="passwordRetryCache"maxEntriesLocalHeap="2000"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true"></cache></ehcache> 復制代碼

#RetryLimitCredentialsMatcher

/** * 驗證器,增加了登錄次數(shù)校驗功能 * 此類不對密碼加密* @author wgc*/ @Component public class RetryLimitCredentialsMatcher extends SimpleCredentialsMatcher { private static final Logger log = LoggerFactory.getLogger(RetryLimitCredentialsMatcher.class);private int maxRetryNum = 5;private EhCacheManager shiroEhcacheManager;public void setMaxRetryNum(int maxRetryNum) {this.maxRetryNum = maxRetryNum;}public RetryLimitCredentialsMatcher(EhCacheManager shiroEhcacheManager) {this.shiroEhcacheManager = shiroEhcacheManager; }@Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { Cache<String, AtomicInteger> passwordRetryCache = shiroEhcacheManager.getCache("passwordRetryCache");String username = (String) token.getPrincipal(); //retry count + 1 AtomicInteger retryCount = passwordRetryCache.get(username); if (null == retryCount) { retryCount = new AtomicInteger(0);passwordRetryCache.put(username, retryCount); }if (retryCount.incrementAndGet() > maxRetryNum) {log.warn("用戶[{}]進行登錄驗證..失敗驗證超過{}次", username, maxRetryNum);throw new ExcessiveAttemptsException("username: " + username + " tried to login more than 5 times in period"); } boolean matches = super.doCredentialsMatch(token, info); if (matches) { //clear retry data passwordRetryCache.remove(username); } return matches; } } 復制代碼

Shiro配置修改

注入CredentialsMatcher

/*** 緩存管理器* @return cacheManager*/@Beanpublic EhCacheManager ehCacheManager(){EhCacheManager cacheManager = new EhCacheManager();cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");return cacheManager;}/*** 限制登錄次數(shù)* @return 匹配器*/@Beanpublic CredentialsMatcher retryLimitCredentialsMatcher() {RetryLimitCredentialsMatcher retryLimitCredentialsMatcher = new RetryLimitCredentialsMatcher(ehCacheManager());retryLimitCredentialsMatcher.setMaxRetryNum(5);return retryLimitCredentialsMatcher;} 復制代碼

realm添加認證器

myShiroRealm.setCredentialsMatcher(retryLimitCredentialsMatcher()); 復制代碼

并發(fā)在線人數(shù)控制實現(xiàn)

KickoutSessionControlFilter

/*** 并發(fā)登錄人數(shù)控制* @author wgc*/ public class KickoutSessionControlFilter extends AccessControlFilter {private static final Logger logger = LoggerFactory.getLogger(KickoutSessionControlFilter.class);/*** 踢出后到的地址*/private String kickoutUrl;/*** 踢出之前登錄的/之后登錄的用戶 默認踢出之前登錄的用戶*/private boolean kickoutAfter = false;/*** 同一個帳號最大會話數(shù) 默認1*/private int maxSession = 1;private String kickoutAttrName = "kickout";private SessionManager sessionManager; private Cache<String, Deque<Serializable>> cache; public void setKickoutUrl(String kickoutUrl) { this.kickoutUrl = kickoutUrl; }public void setKickoutAfter(boolean kickoutAfter) { this.kickoutAfter = kickoutAfter;}public void setMaxSession(int maxSession) { this.maxSession = maxSession; } public void setSessionManager(SessionManager sessionManager) { this.sessionManager = sessionManager; }/*** 設(shè)置Cache的key的前綴*/public void setCacheManager(CacheManager cacheManager) { this.cache = cacheManager.getCache("shiro-kickout-session");}@Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)throws Exception {return false;} @Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response)throws Exception { Subject subject = getSubject(request, response); if(!subject.isAuthenticated() && !subject.isRemembered()){ //如果沒有登錄,直接進行之后的流程 return true;} Session session = subject.getSession();UserInfo user = (UserInfo) subject.getPrincipal(); String username = user.getUsername();Serializable sessionId = session.getId();logger.info("進入KickoutControl, sessionId:{}", sessionId);//讀取緩存 沒有就存入 Deque<Serializable> deque = cache.get(username); if(deque == null) {deque = new LinkedList<Serializable>(); cache.put(username, deque); } //如果隊列里沒有此sessionId,且用戶沒有被踢出;放入隊列if(!deque.contains(sessionId) && session.getAttribute(kickoutAttrName) == null) {//將sessionId存入隊列 deque.push(sessionId); } logger.info("deque.size:{}",deque.size());//如果隊列里的sessionId數(shù)超出最大會話數(shù),開始踢人while(deque.size() > maxSession) { Serializable kickoutSessionId = null; if(kickoutAfter) { //如果踢出后者 kickoutSessionId = deque.removeFirst(); } else { //否則踢出前者 kickoutSessionId = deque.removeLast(); } //踢出后再更新下緩存隊列cache.put(username, deque); try { //獲取被踢出的sessionId的session對象Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));if(kickoutSession != null) { //設(shè)置會話的kickout屬性表示踢出了 kickoutSession.setAttribute(kickoutAttrName, true);}} catch (Exception e) {logger.error(e.getMessage());} } //如果被踢出了,直接退出,重定向到踢出后的地址if (session.getAttribute(kickoutAttrName) != null && (Boolean)session.getAttribute(kickoutAttrName) == true) {//會話被踢出了 try { //退出登錄subject.logout(); } catch (Exception e) { logger.warn(e.getMessage());e.printStackTrace();}saveRequest(request); //重定向 logger.info("用戶登錄人數(shù)超過限制, 重定向到{}", kickoutUrl);String reason = URLEncoder.encode("賬戶已超過登錄人數(shù)限制", "UTF-8");String redirectUrl = kickoutUrl + (kickoutUrl.contains("?") ? "&" : "?") + "shiroLoginFailure=" + reason; WebUtils.issueRedirect(request, response, redirectUrl); return false;} return true; } } 復制代碼

ehcache配置

ehcache-shiro.xml加入

<!-- 用戶隊列緩存10分鐘 --><cache name="shiro-kickout-session"maxEntriesLocalHeap="2000"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true"></cache> 復制代碼

shiro配置

ShiroConfig.java中注入相關(guān)對象

/*** 會話管理器* @return sessionManager*/@Beanpublic DefaultWebSessionManager configWebSessionManager(){DefaultWebSessionManager manager = new DefaultWebSessionManager();// 加入緩存管理器manager.setCacheManager(ehCacheManager());// 刪除過期的sessionmanager.setDeleteInvalidSessions(true);// 設(shè)置全局session超時時間manager.setGlobalSessionTimeout(1 * 60 *1000);// 是否定時檢查sessionmanager.setSessionValidationSchedulerEnabled(true);manager.setSessionValidationScheduler(configSessionValidationScheduler());manager.setSessionIdUrlRewritingEnabled(false);manager.setSessionIdCookieEnabled(true);return manager;}/*** session會話驗證調(diào)度器* @return session會話驗證調(diào)度器*/@Beanpublic ExecutorServiceSessionValidationScheduler configSessionValidationScheduler() {ExecutorServiceSessionValidationScheduler sessionValidationScheduler = new ExecutorServiceSessionValidationScheduler();//設(shè)置session的失效掃描間隔,單位為毫秒sessionValidationScheduler.setInterval(300*1000);return sessionValidationScheduler;}/*** 限制同一賬號登錄同時登錄人數(shù)控制* @return 過濾器*/@Beanpublic KickoutSessionControlFilter kickoutSessionControlFilter() {KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();//使用cacheManager獲取相應的cache來緩存用戶登錄的會話;用于保存用戶—會話之間的關(guān)系的;//這里我們還是用之前shiro使用的redisManager()實現(xiàn)的cacheManager()緩存管理//也可以重新另寫一個,重新配置緩存時間之類的自定義緩存屬性kickoutSessionControlFilter.setCacheManager(ehCacheManager());//用于根據(jù)會話ID,獲取會話進行踢出操作的;kickoutSessionControlFilter.setSessionManager(configWebSessionManager());//是否踢出后來登錄的,默認是false;即后者登錄的用戶踢出前者登錄的用戶;踢出順序。kickoutSessionControlFilter.setKickoutAfter(false);//同一個用戶最大的會話數(shù),默認1;比如2的意思是同一個用戶允許最多同時兩個人登錄;kickoutSessionControlFilter.setMaxSession(1);//被踢出后重定向到的地址;kickoutSessionControlFilter.setKickoutUrl("/login");return kickoutSessionControlFilter;} 復制代碼

shiro過濾鏈中加入并發(fā)登錄人數(shù)過濾器

filterChainDefinitionMap.put("/**", "kickout,user"); 復制代碼

訪問任意鏈接均需要認證通過以及限制并發(fā)登錄次數(shù)

轉(zhuǎn)載于:https://juejin.im/post/5cbe651b6fb9a0322650fbdf

《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的springboot + shiro 尝试登录次数限制与并发登录人数控制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: www.av日韩 | 三级av片| 嫩草导航 | 无码人妻精品一区二区蜜桃色欲 | 亚洲精品视频一区 | 成年人理论片 | 久久入| 日韩在线观看不卡 | 日本天堂免费a | 免费精品 | 日韩www. | 国产人妻一区二区三区四区五区六 | 成人黄色网址在线观看 | 精品在线视频一区二区 | 国产一二视频 | 一本—道久久a久久精品蜜桃 | 人人爽人人草 | 国产女女调教女同 | 久久精品一二三 | 国产精品久久久久久三级 | 在线观看羞羞漫画 | 在线观看国产精品一区 | 国产精品一区二区免费在线观看 | 无法忍受在线观看 | 黄色一机片 | 欧美久久久久久久久久久久 | 污漫网站| 我和我的太阳泰剧在线观看泰剧 | 九九热九九热 | 国产伦精品一区二区三区照片91 | 国产亚洲视频在线 | 国产精品久久久无码一区 | 亚洲欧美另类中文字幕 | 国产精品36p| 极品色av影院 | 青青草原伊人 | 成人精品福利视频 | 免费在线观看黄 | 最新国产视频 | 亚洲av成人精品一区二区三区 | 国产精品免费无遮挡无码永久视频 | 日韩精品久久一区 | 制服丝袜亚洲 | 国产精品中文无码 | 欧美久久综合网 | 亚洲欧美日韩另类 | 欧美激情一区二区三区 | 操丝袜美女视频 | 色在线免费观看 | 中文字幕精品三级久久久 | 国产成人精品无码高潮 | 超碰狠狠干 | av在线超碰 | 奇米色在线 | 国产精选网站 | 久久久久久久久久久久久国产 | 久久国产小视频 | 久久久久人妻精品一区二区三区 | 欧美极品少妇xxxxⅹ裸体艺术 | 亚洲精品码 | 综合久久精品 | 麻豆av电影在线观看 | 一个综合色 | av漫画在线观看 | 国产又色又爽又黄的 | 在线免费观看中文字幕 | 精品免费一区二区三区 | 日本免费一区二区三区四区五六区 | 欧美成人aaaaa | 人妻av中文系列 | 天天摸天天操天天爽 | 日本亚洲精品 | 免费毛片网| 亚洲男人的天堂在线观看 | 中文字幕日韩欧美在线 | 日韩午夜激情视频 | 国产欧美一区二区三区在线看蜜臂 | 99av视频 | 五月天久久 | 性天堂网 | 日日操日日爽 | 少妇人妻综合久久中文字幕 | 美女主播在线观看 | 成人h动漫精品一区二区下载 | 伊人久久青青 | 欧美日韩欧美日韩在线观看视频 | 人人妻人人澡人人爽精品欧美一区 | 国产在线a视频 | 国产激情图片 | 大胸美女被爆操 | 三点尽露的大尺度国产 | 一曲二曲三曲在线观看中文字幕动漫 | 激情六月丁香 | 国产又黄又爽视频 | 国产高清一| 国产精品毛片久久久 | 午夜免费看 | 久久久久久97 | 日韩精品视频一区二区在线观看 |