第二十四章 在线会话管理——《跟我学Shiro》
目录贴:跟我学Shiro目录贴
有时候需要显示当前在线人数、当前在线用户,有时候可能需要强制某个用户下线等;此时就需要获取相应的在线用户并进行一些操作。
本章基于《第十六章 综合实例》代码构建。
会话控制器
- @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) {/*ignore*/}
- redirectAttributes.addFlashAttribute("msg", "强制退出成功!");
- return "redirect:/sessions";
- }
- }
1、list方法:提供了展示所有在线会话列表,通过sessionDAO.getActiveSessions()获取所有在线的会话。
2、forceLogout方法:强制退出某一个会话,此处只在指定会话中设置Constants.SESSION_FORCE_LOGOUT_KEY属性,之后通过ForceLogoutFilter判断并进行强制退出。
此处展示会话列表的缺点是:sessionDAO.getActiveSessions()提供了获取所有活跃会话集合,如果做一般企业级应用问题不大,因为在线用户不多;但是如果应用的在线用户非常多,此种方法就不适合了,解决方案就是分页获取:
- Page<Session> getActiveSessions(int pageNumber, int pageSize);
Page对象除了包含pageNumber、pageSize属性之外,还包含totalSessions(总会话数)、Collection<Session> (当前页的会话)。
分页获取时,如果是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) {/*ignore exception*/}
- 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,其使用数据库存储会话,并分页获取在线会话。
示例源代码:https://github.com/zhangkaitao/shiro-example;可加群 231889722 探讨Spring/Shiro技术。
第二十四章 在线会话管理——《跟我学Shiro》的更多相关文章
- 第二十二章 集成验证码——《跟我学Shiro》
目录贴:跟我学Shiro目录贴 在做用户登录功能时,很多时候都需要验证码支持,验证码的目的是为了防止机器人模拟真实用户登录而恶意访问,如暴力破解用户密码/恶意评论等.目前也有一些验证码比较简单,通过一 ...
- SpringBoot | 第二十四章:日志管理之AOP统一日志
前言 上一章节,介绍了目前开发中常见的log4j2及logback日志框架的整合知识.在很多时候,我们在开发一个系统时,不管出于何种考虑,比如是审计要求,或者防抵赖,还是保留操作痕迹的角度,一般都会有 ...
- 第二十二章 Django会话与表单验证
第二十二章 Django会话与表单验证 第一课 模板回顾 1.基本操作 def func(req): return render(req,'index.html',{'val':[1,2,3...]} ...
- Gradle 1.12用户指南翻译——第二十四章. Groovy 插件
其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...
- “全栈2019”Java多线程第二十四章:等待唤醒机制详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java第二十四章:流程控制语句中决策语句switch下篇
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- 第四章 INI配置——《跟我学Shiro》
转发地址:https://www.iteye.com/blog/jinnianshilongnian-2020820 第四章 INI配置——<跟我学Shiro> 博客分类: 跟我学Shir ...
- 第十五章 单点登录——《跟我学Shiro》
目录贴:跟我学Shiro目录贴 Shiro 1.2开始提供了Jasig CAS单点登录的支持,单点登录主要用于多系统集成,即在多个系统中,用户只需要到一个中央服务器登录一次即可访问这些系统中的任何一个 ...
- SpringBoot | 第二十五章:日志管理之自定义Appender
前言 前面两章节我们介绍了一些日志框架的常见配置及使用实践.一般上,在开发过程中,像log4j2.logback日志框架都提供了很多Appender,基本上可以满足大部分的业务需求了.但在一些特殊需求 ...
随机推荐
- urlrewrite与struts2结合使用基本配置
1.更改web.xml,,,在struts2拦截器前面添加urlrewrite配置信息,,默认是forward的 <filter> <filter-name>UrlRewrit ...
- 多任务案例--文件夹copy.py
import os import multiprocessing def copy_file(q,file_name,new_folder_name,old_folder_name): with op ...
- [ARIA] What is Accessible Name Calculation?
What's in a name? In this lesson, I'll explain the concept of naming interactive elements for screen ...
- 学到了武沛齐讲的Day13完 转义字符
字典 values():值keys():键items():逐条列出 ----------------------------------------------下一day 转义字符 描述\(在行尾时) ...
- 强大的捉包工具Fiddler
Fiddler2出现 creation of the root certificate was not successful 错误 http://www.zhaokeli.com/Article/63 ...
- P3688 [ZJOI2017] 树状数组 【二维线段树】
题目描述:这里有一个写挂的树状数组: 有两种共\(m\)个操作: 输入\(l,r\),在\([l,r]\)中随机选择一个整数\(x\)执行\(\text{Add}(x)\) 输入\(l,r\),询问执 ...
- Unity的资源加载以及AssetBundle的一些坑
https://www.cnblogs.com/sigmadruid/p/4040803.html AssetBundle加载完毕,进行其中Asset的初始化后,不能立即Unload().否则Asse ...
- 数据结构实验之图论三:判断可达性(SDUT 2138)(简单DFS)
#include <bits/stdc++.h> using namespace std; int gra[1002][1005]; int vis[1002]; int n,m; voi ...
- mysql中Numeric类型和int类型的区别
首先记一下,Numeric数字数据只包含 数字.数字数据包括正数.负数.小数.分数和整数 例子如下: Numeric(6,2) Numeric(16,6) Numeric(16,0) 从左到右,第一个 ...
- 作妖 | "该文件没有与之关联的程序来执行该操作..."的解决方法(删除快捷方式小箭头所致)
文章目录 开始作妖 后悔作妖 终结作妖 开始作妖 这是次很作妖的体验过程.因为重新换了个系统,看着桌面上这些快捷方式都有个讨人嫌的小箭头,就在网上搜了搜解决办法. 比如,将下面的内容复制到记事本中,再 ...