shiro自定义过滤器(最大在线人数)

public class KickoutSessionControlFilter extends AccessControlFilter {
    private static final Logger log = LoggerFactory.getLogger(KickoutSessionControlFilter.class);

    /**
     * 踢出后到的地址
     **/
    private String kickoutUrl;

    /**
     * 踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
     **/
    private Boolean kickoutAfter = false;

    /**
     * 同一个账号最大会话数 默认5
     **/
    private int maxSession = 5;

    private SessionManager sessionManager;
    private Cache> cache;

    /**
     * * 表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false;
     *      * (感觉这里应该是对白名单(不需要登录的接口)放行的)
     *      * 如果isAccessAllowed返回true则onAccessDenied方法不会继续执行     因为底层是 (isAccessAllowed || onAccessDenied)
     *      * 这里可以用来判断一些不被通过的链接(个人备注)
     *      * * 表示是否允许访问 ,如果允许访问返回true,否则false;
     * ---------------------
     * 版权声明:本文为CSDN博主「月未明」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
     * 原文链接:https://blog.csdn.net/qq_35981283/article/details/78633692
     * @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表示该拦截器实例已经处理了,将直接返回即可。
     * onAccessDenied是否执行取决于isAccessAllowed的值,如果返回true则onAccessDenied不会执行;如果返回false,执行onAccessDenied
     * 如果onAccessDenied也返回false,则直接返回,不会进入请求的方法(只有isAccessAllowed和onAccessDenied的情况下)
     * */

    @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();
        SysUser sysuser = (SysUser) subject.getPrincipal();
        String username = sysuser.getUsername();
        Serializable sessionId = session.getId();

        //读取缓存 没有就存入
        Deque deque = cache.get(username);

        //如果此用户没有session队列,说明没有登录过,缓存中没有,new一个空队列
        if (null == deque) {
            deque = new LinkedList();
        }

        //如果队列里没有sessionId,且用户没有被踢出,加入队列
        if (!deque.contains(sessionId) && null == session.getAttribute("kickout")) {
            //将sessionId加入队列
            deque.push(sessionId);
            //将用户sessionId队列缓存
            cache.put(username, deque);
        }

        //如果队列中sessionId数量超过最大会话数,开始踢人
        while (deque.size() > maxSession) {
            Serializable kickoutSessionId = null;

            //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
            if (kickoutAfter) {
                kickoutSessionId = deque.removeFirst();
                //踢出后更新缓存队列
                cache.put(username, deque);
            } else {
                kickoutSessionId = deque.removeLast();
                cache.put(username, deque);
            }
            try {
                //获取被踢出的sessionId的session对象
                Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
                if (null != kickoutSession) {
                    //设置会话的kickout属性表示踢出了
                    kickoutSession.setAttribute("kickout", true);
                }
            } catch (Exception e) {
                //面对异常,我们选择忽略
            }
        }

        //如果被踢出了,直接退出,然后重定向到踢出后的地址
        if (null != (Boolean) session.getAttribute("kickout") && (Boolean) session.getAttribute("kickout") == true) {
            //会话被踢出了
            try {
                //退出登录
                subject.logout();//会删除对应的session
            } catch (Exception e) {
                //面对异常,我们选择忽略
            }
            saveRequest(request);//主要作用是 通过response  返回一个新的放上一个新的cookie(JSESSIONID),此处的cookie还没变,下次请求才带新的,比如下面的重定向
            Map resultMap = new HashMap(2);
            //判断是不是Ajax请求
            if ("XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request).getHeader("X-Requested-With"))) {
                resultMap.put("user_status", "300");
                resultMap.put("message", "强制退出,您已经在其他地方登录,请重新登录!");
                //输出json串
                out(response, resultMap);
            } else {
                //重定向  会带着新的cookie
                WebUtils.issueRedirect(request, response, kickoutUrl);
            }
        }
        return true;//true放行
    }

    private void out(ServletResponse response, Map map) throws IOException {
        try {
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
            out.println();
            out.close();
        } catch (Exception e) {
            //面对异常,我们选择忽略
            log.info("[KickoutSessionFilter.class 输出JSON异常,可以忽略。]-[{}]", new Date());
        }
    }

    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_redis_cache");
    }
}

上面的自定义的过滤器要在config里放到过滤链中(anon user perm 其实都是些内置的过滤器)
shiro自定义过滤器(最大在线人数)_第1张图片

要让该过滤器起作用还要在filterChainDefinitionMap put一下,本过滤器是作最大在线人数踢出用的所以放在最前面(规则为(/ ** / ** ) 。自测 / ** 不好用,不知为啥?还请大家指导),当然为了要让该过滤器后面的也正常执行,该过滤器最后要返回true(文开头的那段代码标红的字体),否则后面的/css/** anon等过滤规则就失效了

shiro自定义过滤器(最大在线人数)_第2张图片

你可能感兴趣的:(java,权限)