有时候需要显示当前在线人数、当前在线用户,有时候可能需要强制某个用户下线等;此时就需要获取相应的在线用户并进行一些操作。
本章基于《第十六章 综合实例》代码构建。
会话控制器
1. @RequiresPermissions("session:*")
2. @Controller
3. @RequestMapping("/sessions")
4. public class
5. @Autowired
6. private
7. @RequestMapping()
8. public
9. Collection<Session> sessions = sessionDAO.getActiveSessions();
10. "sessions", sessions);
11. "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. "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
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
2. MGET session.1 session.2…… #根据第一条命令获取的会话ID获取会话数据
ForceLogoutFilter
1. public class ForceLogoutFilter extends
2. protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws
3. 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
10. try
11. //强制退出
12. catch (Exception e) {/*ignore exception*/}
13. "?") ? "&" : "?") + "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. "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,其使用数据库存储会话,并分页获取在线会话。