目录贴:跟我学Shiro目录贴

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

本章基于《第十六章 综合实例》代码构建。

会话控制器

  1. @RequiresPermissions("session:*")
  2. @Controller
  3. @RequestMapping("/sessions")
  4. public class SessionController {
  5. @Autowired
  6. private SessionDAO sessionDAO;
  7. @RequestMapping()
  8. public String list(Model model) {
  9. Collection<Session> sessions =  sessionDAO.getActiveSessions();
  10. model.addAttribute("sessions", sessions);
  11. model.addAttribute("sesessionCount", sessions.size());
  12. return "sessions/list";
  13. }
  14. @RequestMapping("/{sessionId}/forceLogout")
  15. public String forceLogout(@PathVariable("sessionId") String sessionId,
  16. RedirectAttributes redirectAttributes) {
  17. try {
  18. Session session = sessionDAO.readSession(sessionId);
  19. if(session != null) {
  20. session.setAttribute(
  21. Constants.SESSION_FORCE_LOGOUT_KEY, Boolean.TRUE);
  22. }
  23. } catch (Exception e) {/*ignore*/}
  24. redirectAttributes.addFlashAttribute("msg", "强制退出成功!");
  25. return "redirect:/sessions";
  26. }
  27. }

1、list方法:提供了展示所有在线会话列表,通过sessionDAO.getActiveSessions()获取所有在线的会话。

2、forceLogout方法:强制退出某一个会话,此处只在指定会话中设置Constants.SESSION_FORCE_LOGOUT_KEY属性,之后通过ForceLogoutFilter判断并进行强制退出。

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

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

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

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

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

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

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

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

  1. DEL session.123
  2. LREM session.ids 123

获取总活跃会话:

  1. LLEN session.ids

分页获取活跃会话:

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

ForceLogoutFilter

  1. public class ForceLogoutFilter extends AccessControlFilter {
  2. protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
  3. Session session = getSubject(request, response).getSession(false);
  4. if(session == null) {
  5. return true;
  6. }
  7. return session.getAttribute(Constants.SESSION_FORCE_LOGOUT_KEY) == null;
  8. }
  9. protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
  10. try {
  11. getSubject(request, response).logout();//强制退出
  12. } catch (Exception e) {/*ignore exception*/}
  13. String loginUrl = getLoginUrl() + (getLoginUrl().contains("?") ? "&" : "?") + "forceLogout=1";
  14. WebUtils.issueRedirect(request, response, loginUrl);
  15. return false;
  16. }
  17. }

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

登录控制器

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

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

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

Shiro配置spring-config-shiro.xml

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

  1. /** = 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》的更多相关文章

  1. 第二十二章 集成验证码——《跟我学Shiro》

    目录贴:跟我学Shiro目录贴 在做用户登录功能时,很多时候都需要验证码支持,验证码的目的是为了防止机器人模拟真实用户登录而恶意访问,如暴力破解用户密码/恶意评论等.目前也有一些验证码比较简单,通过一 ...

  2. SpringBoot | 第二十四章:日志管理之AOP统一日志

    前言 上一章节,介绍了目前开发中常见的log4j2及logback日志框架的整合知识.在很多时候,我们在开发一个系统时,不管出于何种考虑,比如是审计要求,或者防抵赖,还是保留操作痕迹的角度,一般都会有 ...

  3. 第二十二章 Django会话与表单验证

    第二十二章 Django会话与表单验证 第一课 模板回顾 1.基本操作 def func(req): return render(req,'index.html',{'val':[1,2,3...]} ...

  4. Gradle 1.12用户指南翻译——第二十四章. Groovy 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

  5. “全栈2019”Java多线程第二十四章:等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  6. “全栈2019”Java第二十四章:流程控制语句中决策语句switch下篇

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  7. 第四章 INI配置——《跟我学Shiro》

    转发地址:https://www.iteye.com/blog/jinnianshilongnian-2020820 第四章 INI配置——<跟我学Shiro> 博客分类: 跟我学Shir ...

  8. 第十五章 单点登录——《跟我学Shiro》

    目录贴:跟我学Shiro目录贴 Shiro 1.2开始提供了Jasig CAS单点登录的支持,单点登录主要用于多系统集成,即在多个系统中,用户只需要到一个中央服务器登录一次即可访问这些系统中的任何一个 ...

  9. SpringBoot | 第二十五章:日志管理之自定义Appender

    前言 前面两章节我们介绍了一些日志框架的常见配置及使用实践.一般上,在开发过程中,像log4j2.logback日志框架都提供了很多Appender,基本上可以满足大部分的业务需求了.但在一些特殊需求 ...

随机推荐

  1. fsLayuiPlugin入门使用

    简介 源码下载后,不能直接打开,必须运行在容器下,例如:nginx.tomcat.jetty等容器. 源码中默认配置了nginx容器,可以直接启动nginx访问. 本文主要介绍下载源码后的使用,避免在 ...

  2. Vue 之 slot(插槽)

    前言: vue中关于插槽的文档说明很短,语言又写的很凝练,再加上其和methods,data,computed等常用选项在使用频率.使用先后上的差别,这就有可能造成初次接触插槽的开发者容易产生“算了吧 ...

  3. icpc 银川 H. Delivery Route SPFA优化

    Problem Description Pony is the boss of a courier company. The company needs to deliver packages to ...

  4. Mysql 双主--keepalived

    简介 编辑 Keepalived的作用是检测服务器的状态,如果有一台web服务器宕机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其他服务器代替该服务器的工作 ...

  5. Linux disk 100% busy,谁造成的?

    disk 100% busy,谁造成的? 2016/11/16 vmunix iostat等命令看到的是系统级的统计,比如下例中我们看到/dev/sdb很忙,如果要追查是哪个进程导致的I/O繁忙,应该 ...

  6. svn diff 只显示文件名

    svn diff   --summarize

  7. H-Fashion in Berland

    #include <bits/stdc++.h> using namespace std; int a[1005]; int main() { int n; scanf("%d& ...

  8. ICEM-轴(周期复制网格)

    原视频下载地址:https://yunpan.cn/cqMnfpqQQdZZI  访问密码 802b

  9. nodejs配置QQ企业邮箱

    安装模块 npm install -g nodemailer npm install -g nodemailer-smtp-transport 代码示例 var nodemailer = requir ...

  10. RK3399 修改系统默认语言为简体中文

    CPU:RK3288 系统:Android 7.1 修改系统默认语言为简体中文 diff --git a/build/target/product/full_base.mk b/build/targe ...