本文章摘编、转载需要注明来源 http://write.blog.csdn.net/postedit/8572467
对于spring security我个人是比较喜欢的一个安全框架,我们的系统中一般需要提供强制将用户踢出的功能,这个功能security也有提供,
首先我们要操作需要获取sessionRegistry中认证用户的所有SessionInformation,然后逐个调用SessionInformation里的expireNow()方法,然后ConcurrentSessionFilter
就会执行用户登录注销的功能;对于这个sessionRegistry.removeSessionInformation(sessionInformation
.getSessionId());我不知道是不是我的用法不对,调用了也不会强制注销用户的session,因为官方源代码中说调用removeSessionInformation的时候会让session的监听器响应到,但是我是没成功(望使用这个方式成功的朋友留言告诉下我);下面我们就看为什么调用SessionInformation里的expireNow()方法就能注销用户
/** * 把当前用户踢出系统 */ public void shotOff() { List<SessionInformation> sessionInformations = sessionRegistry .getAllSessions(SpringSecurityManager.getAuthentication() .getPrincipal(), false); for (SessionInformation sessionInformation : sessionInformations) { sessionInformation.expireNow(); // sessionRegistry.removeSessionInformation(sessionInformation .getSessionId()); } }
我们来看下SessionInformation的源码
private Date lastRequest; private final Object principal; private final String sessionId; private boolean expired = false; //~ Constructors =================================================================================================== public SessionInformation(Object principal, String sessionId, Date lastRequest) { Assert.notNull(principal, "Principal required"); Assert.hasText(sessionId, "SessionId required"); Assert.notNull(lastRequest, "LastRequest required"); this.principal = principal; this.sessionId = sessionId; this.lastRequest = lastRequest; } //~ Methods ======================================================================================================== public void expireNow() { this.expired = true; } public Date getLastRequest() { return lastRequest; } public Object getPrincipal() { return principal; } public String getSessionId() { return sessionId; } public boolean isExpired() { return expired; } /** * Refreshes the internal lastRequest to the current date and time. */ public void refreshLastRequest() { this.lastRequest = new Date(); }
看到的是调用expireNow方法时候会将属性expired设置为true;
然后我们来看下需要处理的ConcurrentSessionFilter过滤器的源码
private SessionRegistry sessionRegistry; private String expiredUrl; private LogoutHandler[] handlers = new LogoutHandler[] {new SecurityContextLogoutHandler()}; private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); //~ Methods ======================================================================================================== /** * @deprecated Use constructor which injects the <tt>SessionRegistry</tt>. */ public ConcurrentSessionFilter() { } public ConcurrentSessionFilter(SessionRegistry sessionRegistry) { this(sessionRegistry, null); } public ConcurrentSessionFilter(SessionRegistry sessionRegistry, String expiredUrl) { this.sessionRegistry = sessionRegistry; this.expiredUrl = expiredUrl; } @Override public void afterPropertiesSet() { Assert.notNull(sessionRegistry, "SessionRegistry required"); Assert.isTrue(expiredUrl == null || UrlUtils.isValidRedirectUrl(expiredUrl), expiredUrl + " isn't a valid redirect URL"); } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; HttpSession session = request.getSession(false); if (session != null) { SessionInformation info = sessionRegistry.getSessionInformation(session.getId()); if (info != null) { if (info.isExpired()) { // Expired - abort processing doLogout(request, response); String targetUrl = determineExpiredUrl(request, info); if (targetUrl != null) { redirectStrategy.sendRedirect(request, response, targetUrl); return; } else { response.getWriter().print("This session has been expired (possibly due to multiple concurrent " + "logins being attempted as the same user)."); response.flushBuffer(); } return; } else { // Non-expired - update last request date/time sessionRegistry.refreshLastRequest(info.getSessionId()); } } } chain.doFilter(request, response); } protected String determineExpiredUrl(HttpServletRequest request, SessionInformation info) { return expiredUrl; } private void doLogout(HttpServletRequest request, HttpServletResponse response) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); for (LogoutHandler handler : handlers) { handler.logout(request, response, auth); } } /** * @deprecated use constructor injection instead */ @Deprecated public void setExpiredUrl(String expiredUrl) { this.expiredUrl = expiredUrl; } /** * @deprecated use constructor injection instead */ @Deprecated public void setSessionRegistry(SessionRegistry sessionRegistry) { this.sessionRegistry = sessionRegistry; } public void setLogoutHandlers(LogoutHandler[] handlers) { Assert.notNull(handlers); this.handlers = handlers; } public void setRedirectStrategy(RedirectStrategy redirectStrategy) { this.redirectStrategy = redirectStrategy; }
很明显代码中有遇到if (info.isExpired())逻辑判断,这个时候如果表达式的值为true就会执行里面的doLogout方法,而这个方法里面就会调用我们配置的注销监听器,当前用户在之前认证成功后的状态也会失效了
然后看下xml里怎么配置
<!-- SESSION管理 --> <bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" /> <bean id="concurrentSessionFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter"> <property name="sessionRegistry" ref="sessionRegistry" /> <property name="expiredUrl" value="/apply/skip/restimeout.html" /> <property name="logoutHandlers"> <list> <ref local="logoutHandler" /> </list> </property> </bean> <!-- 注销监听器 --> <bean id="logoutHandler" class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"> <property name="InvalidateHttpSession" value="true" /> </bean>