有时候须要显示当前在线人数、当前在线用户,有时候可能须要强制某个用户下线等;此时就须要获取相应的在线用户并进行一些操做。java
会话控制器git
@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"; } }
一、list方法:提供了展现全部在线会话列表,经过sessionDAO.getActiveSessions()获取全部在线的会话。github
二、forceLogout方法:强制退出某一个会话,此处只在指定会话中设置Constants.SESSION_FORCE_LOGOUT_KEY属性,以后经过ForceLogoutFilter判断并进行强制退出。web
此处展现会话列表的缺点是:sessionDAO.getActiveSessions()提供了获取全部活跃会话集合,若是作通常企业级应用问题不大,由于在线用户很少;可是若是应用的在线用户很是多,此种方法就不适合了,解决方案就是分页获取: redis
Page<Session> getActiveSessions(int pageNumber, int pageSize);
Page对象除了包含pageNumber、pageSize属性以外,还包含totalSessions(总会话数)、Collection<Session> (当前页的会话)。spring
分页获取时,若是是MySQL这种关系数据库存储会话比较好办,若是使用Redis这种数据库能够考虑这样存储:数据库
session.id=会话序列化数据 session.ids=会话id Set列表(接着可使用LLEN获取长度,LRANGE分页获取)
会话建立时(如sessionId=123),那么redis命令以下所示: session
SET session.123 "Session序列化数据" LPUSH session.ids 123
会话删除时(如sessionId=123),那么redis命令以下所示: app
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
测试
一、首先输入http://localhost:8080/chapter24/跳转到登陆页面输入admin/123456登陆;
二、登陆成功后,点击菜单的“会话管理”,能够看到当前在线会话列表:
三、点击“强制退出”按钮,会话相应的用户再点击界面的话会看到以下界面,表示已经被强制退出了:
另外可参考个人ES中的在线会话管理功能:UserOnlineController.java,其使用数据库存储会话,并分页获取在线会话。
示例源代码:https://github.com/zhangkaitao/shiro-example;
转载自----------------http://jinnianshilongnian.iteye.com/blog/2047643