- 2、注册过滤器
/** * 并发登录控制 * * @return */ @Bean public KickoutSessionFilter kickoutSessionControlFilter() { KickoutSessionFilter kickoutSessionControlFilter = new KickoutSessionFilter(); // 用于根据会话ID,获取会话进行踢出操作的; kickoutSessionControlFilter.setSessionManager(sessionManager()); // 使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的; kickoutSessionControlFilter.setCacheManager(ehCacheManager()); // 是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户; kickoutSessionControlFilter.setKickoutAfter(false); // 同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录; kickoutSessionControlFilter.setMaxSession(1); // 被踢出后重定向到的地址; kickoutSessionControlFilter.setKickoutUrl("/login?kickout=1"); return kickoutSessionControlFilter; }
其中用到的 session 管理器,和 ehcache 管理器在之前的博客中都有讲到,本次不再赘述
我们将踢出的用户重定向到登录界面,并携带参数 kickout
- 3、注入自定义过滤器
/** * 地址过滤器 * * @param securityManager * @return */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 设置securityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 设置登录url shiroFilterFactoryBean.setLoginUrl("/login"); // 设置主页url shiroFilterFactoryBean.setSuccessUrl("/"); // 设置未授权的url shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 自定义拦截器限制并发人数 LinkedHashMap<String, Filter> filtersMap = new LinkedHashMap<>(); // 限制同一帐号同时在线的个数 filtersMap.put("kickout", kickoutSessionControlFilter()); shiroFilterFactoryBean.setFilters(filtersMap); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 注销登录 filterChainDefinitionMap.put("/loginOut", "logout"); // 开放登录接口 filterChainDefinitionMap.put("/doLogin", "anon"); // 开放获取登录验证码接口 filterChainDefinitionMap.put("/kaptcha/**", "anon"); // 开放Api接口 filterChainDefinitionMap.put("/api/**", "anon"); // 开放微信接口 filterChainDefinitionMap.put("/weixin/**", "anon"); // 开放websocket接口 filterChainDefinitionMap.put("/websocket/**", "anon"); // 开放接口文档 filterChainDefinitionMap.put("/doc.html", "anon"); filterChainDefinitionMap.put("/service-worker.js", "anon"); filterChainDefinitionMap.put("/swagger-resources/**", "anon"); filterChainDefinitionMap.put("/webjars/**", "anon"); filterChainDefinitionMap.put("/v2/**", "anon"); // 开放静态资源 filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/img/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/layui/**", "anon"); filterChainDefinitionMap.put("/layuimini/**", "anon"); filterChainDefinitionMap.put("/module/**", "anon"); filterChainDefinitionMap.put("/upload/**", "anon"); // 其余url全部拦截,必须放在最后 filterChainDefinitionMap.put("/**", "kickout,user"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }
我们在 ShiroFilterFactoryBean 对象中注入了自定义过滤器,并在最后的地址拦截规则中增加了 kickout,即使我们开启了记住登录功能,该用户也会被踢下线
- 4、提示信息
我们在登录页面中,需要获取地址中是否有 kickout 参数
// 是否被挤下线 if(location.href.indexOf("kickout") > 0){ setTimeout(function () { layNotify.notice({ title: "登录提示", type: "error", message: '您的账户已在另一台设备上登录,如非本人操作,请立即修改密码!' }); }, 1000) }
这样后面登录的用户就会挤掉前面登录用户,导致前面登录的用户被踢下线了