From 7f679837aaeedb4bd71853b51173c4dca2193511 Mon Sep 17 00:00:00 2001 From: xiangwb Date: Sun, 8 Apr 2018 16:13:38 +0800 Subject: [PATCH] merge --- .../shiro/KickoutSessionControlFilter.java | 125 ++++++++++++++++++ .../java/com/xwbing/shiro/MyShiroRealm.java | 12 +- .../java/com/xwbing/shiro/ShiroConfig.java | 23 +++- .../src/main/resources/static/kickout.html | 10 ++ 4 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 boot-web/src/main/java/com/xwbing/shiro/KickoutSessionControlFilter.java create mode 100644 boot-web/src/main/resources/static/kickout.html diff --git a/boot-web/src/main/java/com/xwbing/shiro/KickoutSessionControlFilter.java b/boot-web/src/main/java/com/xwbing/shiro/KickoutSessionControlFilter.java new file mode 100644 index 0000000..adb0592 --- /dev/null +++ b/boot-web/src/main/java/com/xwbing/shiro/KickoutSessionControlFilter.java @@ -0,0 +1,125 @@ +package com.xwbing.shiro; + +import com.xwbing.domain.entity.sys.SysUser; +import org.apache.shiro.cache.Cache; +import org.apache.shiro.cache.CacheManager; +import org.apache.shiro.session.Session; +import org.apache.shiro.session.mgt.DefaultSessionKey; +import org.apache.shiro.session.mgt.SessionManager; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.web.filter.AccessControlFilter; +import org.apache.shiro.web.util.WebUtils; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.Serializable; +import java.util.Deque; +import java.util.LinkedList; + +/** + * 项目名称: boot-module-demo + * 创建时间: 2018/4/3 18:02 + * 作者: xiangwb + * 说明: 防止二次登陆的功能 + */ +public class KickoutSessionControlFilter extends AccessControlFilter { + private String kickoutUrl; // 踢出后到的地址 + private boolean kickoutAfter; // 踢出之前登录的/之后登录的用户 默认踢出之前登录的用户 + private int maxSession = 1; // 同一个帐号最大会话数 默认1 + private SessionManager sessionManager; + private Cache> 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; + } + + public void setCacheManager(CacheManager cacheManager) { + this.cache = cacheManager.getCache("shiro-kickout-session"); + } + + /** + * 是否允许访问,返回true表示允许 + * + * @param request + * @param response + * @param mappedValue + * @return + * @throws Exception + */ + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { + return false; + } + + /** + * 表示访问拒绝时是否自己处理,如果返回true表示自己不处理且继续拦截器链执行,返回false表示自己已经处理了(比如重定向到另一个页面)。 + * + * @param request + * @param response + * @return + * @throws Exception + */ + @Override + protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { + Subject subject = getSubject(request, response); + if (!subject.isAuthenticated() && !subject.isRemembered()) { + // 如果没有登录,直接进行之后的流程 + return true; + } + Session session = subject.getSession(); + Object object = subject.getPrincipal(); + SysUser sysUser = (SysUser) object; + Serializable sessionId = session.getId(); + // 初始化用户的队列放到缓存里 + Deque deque = cache.get(sysUser.getUserName()); + if (deque == null || deque.size() == 0) { + deque = new LinkedList<>(); + cache.put(sysUser.getUserName(), deque); + } + // 如果队列里没有此sessionId,且用户没有被踢出;放入队列 + if (!deque.contains(sessionId) && session.getAttribute("kickout") == null) { + deque.push(sessionId); + } + // 如果队列里的sessionId数超出最大会话数,开始踢人 + while (deque.size() > maxSession) { + Serializable kickoutSessionId; + if (kickoutAfter) { // 如果踢出后者 + kickoutSessionId = deque.removeFirst(); + } else { // 否则踢出前者 + kickoutSessionId = deque.removeLast(); + } + try { + Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId)); + if (kickoutSession != null) { + // 设置会话的kickout属性表示踢出了 + kickoutSession.setAttribute("kickout", true); + } + } catch (Exception e) {// ignore exception + } + } + // 如果被踢出了,直接退出,重定向到踢出后的地址 + if (session.getAttribute("kickout") != null) { + // 会话被踢出了 + try { + subject.logout(); + } catch (Exception e) { // ignore + } + saveRequest(request); + WebUtils.issueRedirect(request, response, kickoutUrl); + return false; + } + return true; + } +} diff --git a/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java b/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java index 087ed75..d717ec8 100644 --- a/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java +++ b/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java @@ -8,8 +8,6 @@ import com.xwbing.service.sys.SysAuthorityService; import com.xwbing.service.sys.SysRoleService; import com.xwbing.service.sys.SysUserService; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; @@ -92,11 +90,11 @@ public class MyShiroRealm extends AuthorizingRealm { protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) { UsernamePasswordCaptchaToken captchaToken = (UsernamePasswordCaptchaToken) authenticationToken; //1.校验验证码 - String captcha = captchaToken.getCaptcha(); - String imgCode = (String) SecurityUtils.getSubject().getSession().getAttribute(CommonConstant.KEY_CAPTCHA); - if (StringUtils.isEmpty(captcha) || !captcha.equalsIgnoreCase(imgCode)) { - throw new AuthenticationException("验证码错误"); - } +// String captcha = captchaToken.getCaptcha(); +// String imgCode = (String) SecurityUtils.getSubject().getSession().getAttribute(CommonConstant.KEY_CAPTCHA); +// if (StringUtils.isEmpty(captcha) || !captcha.equalsIgnoreCase(imgCode)) { +// throw new AuthenticationException("验证码错误"); +// } //2.查是否有此用户 SysUser user = sysUserService.getByUserName(captchaToken.getUsername()); if (user == null) { diff --git a/boot-web/src/main/java/com/xwbing/shiro/ShiroConfig.java b/boot-web/src/main/java/com/xwbing/shiro/ShiroConfig.java index bb1df4e..d20631e 100644 --- a/boot-web/src/main/java/com/xwbing/shiro/ShiroConfig.java +++ b/boot-web/src/main/java/com/xwbing/shiro/ShiroConfig.java @@ -67,6 +67,7 @@ public class ShiroConfig { Map filters = new HashMap<>(); filters.put("sessionFilter", sessionFilter()); + filters.put("kickout", kickoutSessionControlFilter()); filters.put("authc", urlPermissionsFilter()); filters.put("anon", new AnonymousFilter()); shiroFilterFactoryBean.setFilters(filters); @@ -95,7 +96,7 @@ public class ShiroConfig { //登出 chains.put("/user/logout", "anon"); //必须通过验证或者rememberMe - chains.put("/**", "user,authc,sessionFilter"); + chains.put("/**", "user,authc,sessionFilter,kickout"); shiroFilterFactoryBean.setFilterChainDefinitionMap(chains); return shiroFilterFactoryBean; } @@ -203,6 +204,26 @@ public class ShiroConfig { return new SessionFilter(); } + ///////////////////////kickoutSessionControlFilter/////////////////// + @Bean + public FilterRegistrationBean kickoutSessionControlFilterRegistrationBean(KickoutSessionControlFilter kickoutSessionControlFilter) { + FilterRegistrationBean registration = new FilterRegistrationBean(kickoutSessionControlFilter); + //设置不自动在在配置时执行,默认是true + registration.setEnabled(false); + return registration; + } + + @Bean + public KickoutSessionControlFilter kickoutSessionControlFilter() { + KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter(); + kickoutSessionControlFilter.setCacheManager(cacheManager()); + kickoutSessionControlFilter.setSessionManager(sessionManager()); + kickoutSessionControlFilter.setKickoutAfter(false); + kickoutSessionControlFilter.setMaxSession(1); + kickoutSessionControlFilter.setKickoutUrl("/kickout.html"); + return kickoutSessionControlFilter; + } + /** * Shiro生命周期处理器 * diff --git a/boot-web/src/main/resources/static/kickout.html b/boot-web/src/main/resources/static/kickout.html new file mode 100644 index 0000000..c0f4cf9 --- /dev/null +++ b/boot-web/src/main/resources/static/kickout.html @@ -0,0 +1,10 @@ + + + + + kickout + + +

你的账号在另一个地方登录,请确认是否本人登录!

+ + \ No newline at end of file