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

2023-11-11

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<String, Deque<Serializable>> 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<Serializable> deque = cache.get(username);

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

        //如果队列里没有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<String, String> resultMap = new HashMap<String, String>(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<String, String> 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 其实都是些内置的过滤器)
在这里插入图片描述

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

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

shiro自定义过滤器(最大在线人数) 的相关文章

随机推荐

  • Scrapy框架中的crawlspider爬虫

    1 crawlspider是什么 在spider中要寻找下一页的url地址或者内容的url地址 想想是否有简单的方法省略寻找url的过程 思路 从response中提取所有的满足规则的url地址 自动的构造自己requests请求 发送给引
  • ARP工作原理以及ARP欺骗、中间人攻击

    目录 什么是ARP ARP的作用是什么 ARP的工作原理 ARP欺骗 ARP欺骗 中间人攻击实验 什么是ARP ARP的作用是什么 ARP即地址解析协议 属于网络层 作用是根据已知的IP地址解析获得其对应的MAC地址 ARP的工作原理 AR
  • 深度解读:区块链技术+智能医疗的核心优势

    2017年以来 数字货币热潮席卷全球 走进了大众的视野 作为比特币底层技术的区块链也展现出了其颠覆不同行业的潜力 医疗行业被认为是区块链技术最有潜力的应用领域之一 随着全球数字化进程的高速发展 如何保障包含大量患者隐私的医疗数据不被泄露变得
  • 深入使用noexcept

    深入使用noexcept 简介 好处 坏处 适用场景 不适用场景 实验结果 总结 参考资料 简介 noexcept是C 11引入的 表明函数是否会抛出异常 正确使用它可以优化性能 错误使用则会带来麻烦 noexcept使用语法有两种 noe
  • cookie字符串转为CookieCollection

    string cookiestr uin 123456789 skey abcabc 参考 下例都无用 httpwebrequest AllowAutoRedirect false 禁止httpwebrequest自动跳转 string A
  • Java程序员:内事不决问百度,外事不决问谷歌,一遇面试就变捞

    前言 我还记得大学毕业刚入职那会儿 进了一家不大不小的公司 拿着一份仅仅能养活自己的薪水 做着日复一日的基操 聊天扯淡 优哉游哉 甚不快活 仍还记得 进入公司之初 一位公司的 老 程序员给我传输了一个经验 内事不决问百度 外事不决问谷歌 多
  • WinAPI: FindWindow、FindWindowEx - 查找窗口

    FindWindow lpClassName 窗口的类名 lpWindowName PChar 窗口的标题 HWND 返回窗口的句柄 失败返回 0 FindWindowEx 比 FindWindow 多出两个句柄参数 FindWindowE
  • SQL、presto将一对多出现重复的字段新添一列表示

    SQL presto将一对多出现重复的字段新添一列表示 例如一个用户 id 在多个渠道都有授信金额 amount 我希望展示出来这个用户在所有渠道的授信金额 但 select id amount from table 可能出现的结果是 一个
  • 【笔记】 C++中 Prefix/Suffix of a Literal

    C Primer English Version Page40 Table 2 2 是需要记忆一下的 有些记不住 就记了下笔记 Character and Character String Literals Prefix Meaning T
  • 照片生成3D虚拟数字人,虚拟形象主播搭建(软件+教程)

    1 一张照片快速生成3D虚拟数字人 无需建模和动画基础 2 真人驱动数字人 数字人代替真人出镜 真人无需露脸也能轻松做直播 3 形象丰富 角色自定义 场景自定义 可以每天都换着花样的直播 5 操作简单 最重要的是不需要动作捕捉设备 5 适用
  • 让专注的思维成为习惯

    朱自清在 匆匆 中曾描述 洗手的时候 日子从水盆里过去 吃饭的时候 日子从饭碗里过去 默默时 便从凝然的双眼前过去 我想说 当我们思考一天时间都用来做什么的时候 真的发现暗时间很多 真正的用于学习or工作的时间并不多 下面是我统计某一天时间
  • Java中的数组复制:System.arraycopy()方法

    arraycopy 方法中有五个参数 第一个 源数组 第二个 源数字的复制起始位置 第三个 目标数组 第四个 数字的起始位置 第五个 复制的长度 public class ArrayTest04 public static void mai
  • 在 Linux 文件系统中使用 attr 添加扩展属性

    我使用开源的 XFS 文件系统是为了其扩展属性带来的小小便利 扩展属性是一种为我的数据添加上下文的独特方式 文件系统 是一个描述你的计算机怎样跟踪你创建的所有文件的完美词语 你的计算机存储有大量的数据 无论是文档 配置文件还是数以千计的照片
  • 以太网相关

    1 隔离变压器作用 信号传输 阻抗匹配 波形修复 信号杂波抑制和高电压隔离作用 2 MAC到PHY常用接口种类 有哪些信号 多少个信号 以太网PHY MAC接口模式 RGMII RMII MII SGMII SMII GMII XGMII
  • 服务器芯片和超算芯片,英特尔芯片将挑战Nvidia GPU和谷歌 TPU

    英特尔芯片似乎有了一些明显的软肋 其中之一就是缺乏高端图像处理器 而这对游戏 虚拟实境和机器学习都极为重要 但英特尔也确实有两个强大的替代选择 在机器学习和超算领域迎战GPU等极具竞争性的芯片 2018年 英特尔可能会推出一个更快 更节能的
  • 使用mybatis来操作oracle

    1 使用spring的初始化工具来构建项目 2 添加依赖 3 项目结构 4 详细文件 4 1配置文件 server port 8888 spring datasource driver class name oracle jdbc driv
  • 网络安全进阶篇之流量加密(十三章-2)MSF流量加密躲避检测

    文章目录 一 为什么要加密 二 创建证书 三 上线MSF 四 抓取流量测试 五 不上线的情况 5 1 经过测试win7系统无法上线 5 2 看看配置是否正确 一 为什么要加密 为了防止主机被入侵 现在大部分的内网环境都装有流量审计工具 专门
  • 【Git 教程系列第 26 篇】Mac 升级系统到 Ventura 后,Git 公钥报 Permission denied 错误问题的解决方案

    这是 Git 教程系列第 26 篇 如果觉得有用的话 欢迎关注专栏 注 如果你是因为升级系统到 Ventura 后遇到的这个问题 可以直接看第三步的解决方案 前两步是我自己的写作习惯 只是记录一下这个过程 当然你也能从中读到更多的信息 文章
  • web端播放视频之rtsp协议转HLS

    rtsp协议转HLS rtsp转hls协议 一 前言 1 传统安防行业 2 新兴直播行业 二 实现 1 rtsp转为HLS 2 提供http服务 3 组件封装dll 三 测试 1 使用vlc测试hls 2 使用nginx测试hls 3 实时
  • shiro自定义过滤器(最大在线人数)

    public class KickoutSessionControlFilter extends AccessControlFilter private static final Logger log LoggerFactory getLo