Shiro 入门教程 - Shiro 在线会话管理

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

在线会话管理

有时候需要显示当前在线人数、当前在线用户,有时候可能需要强制某个用户下线等;此时就需要获取相应的在线用户并进行一些操作。

会话控制器

    @RequiresPermissions("session:*")
    @Controller
    @RequestMapping("/sessions")
    public class SessionController {
        @Autowired
        private SessionDAO sessionDAO;
        @RequestMapping()
        public String list(Model model) {
            Collection<Session> sessions =  sessionDAO.getActiveSessions();
            model.addAttribute("sessions", sessions);
            model.addAttribute("sesessionCount", sessions.size());
            return "sessions/list";
        }
        @RequestMapping("/{sessionId}/forceLogout")
        public String forceLogout(@PathVariable("sessionId") String sessionId, 
            RedirectAttributes redirectAttributes) {
            try {
                Session session = sessionDAO.readSession(sessionId);
                if(session != null) {
                    session.setAttribute(
                        Constants.SESSION_FORCE_LOGOUT_KEY, Boolean.TRUE);
                }
            } catch (Exception e) {/*忽略*/}
            redirectAttributes.addFlashAttribute("msg", "强制退出成功!");
            return "redirect:/sessions";
        }
    }
  1. list 方法:提供了展示所有在线会话列表,通过 sessionDAO.getActiveSessions() 获取所有在线的会话。
  2. forceLogout 方法:强制退出某一个会话,此处只在指定会话中设置 Constants.SESSION_FORCE_LOGOUT_KEY 属性,之后通过 ForceLogoutFilter 判断并进行强制退出。

此处展示会话列表的缺点是:sessionDAO.getActiveSessions() 提供了获取所有活跃会话集合,如果做一般企业级应用问题不大,因为在线用户不多;但是如果应用的在线用户非常多,此种方法就不适合了,解决方案就是分页获取:

Java代码

    Page<Session> getActiveSessions(int pageNumber, int pageSize);

Page 对象除了包含 pageNumber、pageSize 属性之外,还包含 totalSessions(总会话数)、Collection (当前页的会话)。

分页获取时,如果是 MySQL 这种关系数据库存储会话比较好办,如果使用 Redis 这种数据库可以考虑这样存储:

    session.id=会话序列化数据
    session.ids=会话id Set列表(接着可以使用LLEN获取长度,LRANGE分页获取)

会话创建时(如 sessionId=123),那么 redis 命令如下所示:

    SET session.123 "Session序列化数据"
    LPUSH session.ids 123

会话删除时(如 sessionId=123),那么 redis 命令如下所示:

    DEL session.123
    LREM session.ids 123

获取总活跃会话:

    LLEN session.ids

分页获取活跃会话:

    LRANGE key 0 10 #获取到会话ID
    MGET session.1 session.2……  #根据第一条命令获取的会话ID获取会话数据

ForceLogoutFilter

     public class ForceLogoutFilter extends AccessControlFilter {       protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {           Session session = getSubject(request, response).getSession(false);           if(session == null) {               return true;           }           return session.getAttribute(Constants.SESSION_FORCE_LOGOUT_KEY) == null;       }       protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {           try {               getSubject(request, response).logout();    /*强制退出*/          } catch (Exception e) {/*忽略异常*/}
             String loginUrl = getLoginUrl() + (getLoginUrl().contains("?") ? "&" : "?") + "forceLogout=1";           WebUtils.issueRedirect(request, response, loginUrl);           return false;       }   } 

强制退出拦截器,如果用户会话中存在 Constants.SESSION_FORCE_LOGOUT_KEY 属性,表示被管理员强制退出了;然后调用 Subject.logout() 退出,且重定向到登录页面(自动拼上 fourceLogout 请求参数)。

登录控制器

在 LoginController 类的 showLoginForm 方法中最后添加如下代码:

    if(req.getParameter("forceLogout") != null) {
        model.addAttribute("error", "您已经被管理员强制退出,请重新登录");
    }

即如果有请求参数 forceLogout 表示是管理员强制退出的,在界面上显示相应的信息。

Shiro 配置 spring-config-shiro.xml

和之前的唯一区别是在 shiroFilter 中的 filterChainDefinitions 拦截器链定义中添加了 forceLogout 拦截器:

    /** = forceLogout,user,sysUser

测试

1、首先输入 http://localhost:8080/chapter24/ 跳转到登录页面输入 admin/123456 登录;

2、登录成功后,点击菜单的 “会话管理”,可以看到当前在线会话列表:

3、点击 “强制退出” 按钮,会话相应的用户再点击界面的话会看到如下界面,表示已经被强制退出了:

另外可参考我的 ES 中的在线会话管理功能:UserOnlineController.java,其使用数据库存储会话,并分页获取在线会话。

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值