分享

第二十四章 在线会话管理——《跟我学Shiro》

 一本正经地胡闹 2019-08-31

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

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

会话控制器

Java代码 收藏代码
  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. }   

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

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

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

Java代码 收藏代码
  1. Page<Session> getActiveSessions(int pageNumber, int pageSize);  

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

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

Java代码 收藏代码
  1. session.id=会话序列化数据  

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

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

Java代码 收藏代码
  1. SET session.123 "Session序列化数据"  

  2. LPUSH session.ids 123      

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

Java代码 收藏代码
  1. DEL session.123  

  2. LREM session.ids 123      

获取总活跃会话:

Java代码 收藏代码
  1. LLEN session.ids  

分页获取活跃会话: 

Java代码 收藏代码
  1. LRANGE key 0 10 #获取到会话ID  

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

ForceLogoutFilter

Java代码 收藏代码
  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方法中最后添加如下代码: 

Java代码 收藏代码
  1. if(req.getParameter("forceLogout") != null) {  

  2.     model.addAttribute("error", "您已经被管理员强制退出,请重新登录");  

  3. }   

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

Shiro配置spring-config-shiro.xml

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

Java代码 收藏代码
  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技术。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多