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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

案例代码:springboot+shiro配置同一用户多设备登录最大会话数

發(fā)布時(shí)間:2024/9/30 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 案例代码:springboot+shiro配置同一用户多设备登录最大会话数 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

另一鏈接:
springboot+springsecurity配置登錄后踢出前一個(gè)登錄用戶

1.自定義登陸過濾器類:

/*** 登錄帳號(hào)控制過濾器* */ public class KickoutSessionFilter extends AccessControlFilter {private final static ObjectMapper objectMapper = new ObjectMapper();/*** 同一個(gè)用戶最大會(huì)話數(shù)**/private int maxSession = -1;/*** 踢出之前登錄的/之后登錄的用戶 默認(rèn)false踢出之前登錄的用戶**/private Boolean kickoutAfter = false;/*** 踢出后到的地址**/private String kickoutUrl;private SessionManager sessionManager;private Cache<String, Deque<Serializable>> cache;@Overrideprotected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o)throws Exception{return false;}@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception{Subject subject = getSubject(request, response);if (!subject.isAuthenticated() && !subject.isRemembered() || maxSession == -1){// 如果沒有登錄或用戶最大會(huì)話數(shù)為-1,直接進(jìn)行之后的流程return true;}try{Session session = subject.getSession();// 當(dāng)前登錄用戶SysUser user = ShiroUtils.getSysUser();String loginName = user.getLoginName();Serializable sessionId = session.getId();// 讀取緩存用戶 沒有就存入Deque<Serializable> deque = cache.get(loginName);if (deque == null){// 初始化隊(duì)列deque = new ArrayDeque<Serializable>();}// 如果隊(duì)列里沒有此sessionId,且用戶沒有被踢出;放入隊(duì)列if (!deque.contains(sessionId) && session.getAttribute("kickout") == null){// 將sessionId存入隊(duì)列deque.push(sessionId);// 將用戶的sessionId隊(duì)列緩存cache.put(loginName, deque);}// 如果隊(duì)列里的sessionId數(shù)超出最大會(huì)話數(shù),開始踢人while (deque.size() > maxSession){// 是否踢出后來登錄的,默認(rèn)是false;即后者登錄的用戶踢出前者登錄的用戶;Serializable kickoutSessionId = kickoutAfter ? deque.removeFirst() : deque.removeLast();// 踢出后再更新下緩存隊(duì)列cache.put(loginName, deque);try{// 獲取被踢出的sessionId的session對(duì)象Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));if (null != kickoutSession){// 設(shè)置會(huì)話的kickout屬性表示踢出了kickoutSession.setAttribute("kickout", true);}}catch (Exception e){// 面對(duì)異常,我們選擇忽略}}// 如果被踢出了,(前者或后者)直接退出,重定向到踢出后的地址if (session.getAttribute("kickout") != null && (Boolean) session.getAttribute("kickout") == true){// 退出登錄subject.logout();saveRequest(request);return isAjaxResponse(request, response);}return true;}catch (Exception e){return isAjaxResponse(request, response);}}private boolean isAjaxResponse(ServletRequest request, ServletResponse response) throws IOException{HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse res = (HttpServletResponse) response;if (ServletUtils.isAjaxRequest(req)){AjaxResult ajaxResult = AjaxResult.error("您已在別處登錄,請(qǐng)您修改密碼或重新登錄");ServletUtils.renderString(res, objectMapper.writeValueAsString(ajaxResult));}else{WebUtils.issueRedirect(request, response, kickoutUrl);}return false;}public void setMaxSession(int maxSession){this.maxSession = maxSession;}public void setKickoutAfter(boolean kickoutAfter){this.kickoutAfter = kickoutAfter;}public void setKickoutUrl(String kickoutUrl){this.kickoutUrl = kickoutUrl;}public void setSessionManager(SessionManager sessionManager){this.sessionManager = sessionManager;}// 設(shè)置Cache的key的前綴public void setCacheManager(CacheManager cacheManager){// 必須和ehcache緩存配置中的緩存name一致this.cache = cacheManager.getCache(ShiroConstants.SYS_USERCACHE);} }

shiro配置類

@Configuration public class ShiroConfig {/*** 同一個(gè)用戶多設(shè)備登錄限制*/public KickoutSessionFilter kickoutSessionFilter(){KickoutSessionFilter kickoutSessionFilter = new KickoutSessionFilter();kickoutSessionFilter.setCacheManager(getEhCacheManager());kickoutSessionFilter.setSessionManager(sessionManager());// 同一個(gè)用戶最大的會(huì)話數(shù),默認(rèn)-1無限制;比如2的意思是同一個(gè)用戶允許最多同時(shí)兩個(gè)人登錄kickoutSessionFilter.setMaxSession(maxSession);// 是否踢出后來登錄的,默認(rèn)是false;即后者登錄的用戶踢出前者登錄的用戶;踢出順序kickoutSessionFilter.setKickoutAfter(kickoutAfter);// 被踢出后重定向到的地址;kickoutSessionFilter.setKickoutUrl("/login?kickout=1");return kickoutSessionFilter;}/*** Shiro過濾器配置*/@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// Shiro的核心安全接口,這個(gè)屬性是必須的shiroFilterFactoryBean.setSecurityManager(securityManager);// 身份認(rèn)證失敗,則跳轉(zhuǎn)到登錄頁面的配置shiroFilterFactoryBean.setLoginUrl(loginUrl);// 權(quán)限認(rèn)證失敗,則跳轉(zhuǎn)到指定頁面shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);// Shiro連接約束配置,即過濾鏈的定義LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();// 對(duì)靜態(tài)資源設(shè)置匿名訪問filterChainDefinitionMap.put("/favicon.ico**", "anon");// 系統(tǒng)權(quán)限列表// filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll());Map<String, Filter> filters = new LinkedHashMap<String, Filter>();filters.put("captchaValidate", captchaValidateFilter());filters.put("kickout", kickoutSessionFilter());//注意這里,注冊(cè)自己定義的會(huì)話數(shù)量控制過濾器// 注銷成功,則跳轉(zhuǎn)到指定頁面filters.put("logout", logoutFilter());shiroFilterFactoryBean.setFilters(filters);// 所有請(qǐng)求需要認(rèn)證filterChainDefinitionMap.put("/**", "user,kickout,onlineSession,syncOnlineSession");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}

總結(jié)

以上是生活随笔為你收集整理的案例代码:springboot+shiro配置同一用户多设备登录最大会话数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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