用于Spring Security和/或Spring BlazeDS集成的会话管理(和查杀)的集中系统

我很难实现客户要求的function。 简而言之,他们希望能够通过管理员方面退出他们选择的任何客户。 该应用程序使用Flex作为前端技术并通过AMF访问服务器。 服务器端使用Spring Security和Spring BlazeDS Integration。

基本上问题是:Spring Security和/或Spring BlazeDS Integration是否为开箱即用的会话管理(和查杀)提供了任何集中式系统?

出于概念validation的目的,我尝试注销所有用户并使用以下代码终止所有会话:

package xxx.xxx.xxx; import java.util.List; import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.session.SessionInformation; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.userdetails.User; import flex.messaging.MessageBroker; import flex.messaging.security.LoginCommand; public class SessionServiceImpl { private static final Log log = LogFactory.getLog(SessionServiceImpl.class); private SessionRegistry sessionRegistry; private MessageBroker messageBroker; public SessionRegistry getSessionRegistry() { return sessionRegistry; } @Autowired public void setSessionRegistry(SessionRegistry sessionRegistry) { log.debug("sessionregistry set"); this.sessionRegistry = sessionRegistry; } public MessageBroker getMessageBroker() { return messageBroker; } @Autowired public void setMessageBroker(MessageBroker messageBroker) { log.debug("messagebroker set"); this.messageBroker = messageBroker; } public void logoutUser(String userName) { log.debug("Logging out user by username: "+userName); List principals = null; if(sessionRegistry != null){ principals = sessionRegistry.getAllPrincipals(); }else{ log.debug("sessionRegistry null"); } if(principals != null){ for (Object object : principals) { User user = (User)object; // get single users all sessions List sessions = sessionRegistry.getAllSessions(user, false); log.debug("Sessions list size: "+sessions.size()); if(messageBroker != null){ LoginCommand command = messageBroker.getLoginManager().getLoginCommand(); UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user, user.getPassword()); command.logout(usernamePasswordAuthenticationToken); for (SessionInformation sessionInformation : sessions) { log.debug(ReflectionToStringBuilder.toString(sessionInformation)); sessionInformation.expireNow(); sessionRegistry.removeSessionInformation(sessionInformation.getSessionId()); } }else{ log.debug("messageBroker null"); } if(object != null){ log.debug(ReflectionToStringBuilder.toString(object)); }else{ log.debug("object null"); } } }else{ log.debug("principals null"); } } } 

不幸的是,上面的代码不起作用。 据我所知,这是因为两件事:

A)LoginCommand不是“应用程序范围”,而是绑定到当前会话,因此它将尝试仅注销当前会话(管理员正在使用的会话)并且忽略其他会话

B)sessionInformation.expireNow()尝试使会话到期但如果用户设法在会话失效之前发出请求,则会话不会被销毁

从文档中我可以看到会话可以通过session.invalidate()直接失效,但似乎我无法访问所有会话对象。

实现此类function的最快或最智能的方法是什么?

此致,Jukka

我的方法是间接会话失效。

使用ConcurrentSessionControl安全选项限制每个用户1个会话。 编写自定义SessionAuthenticationStrategy,检查用户是否已被标记,并在需要时使会话无效。 请注意,会话策略应该在例如usernamepasswordfilter创建新会话之前执行。

您可以使用数据库或类似静态类来保存用户名。 此外,您可能还希望拥有某种时间戳,这些时间戳只会在标记后的x分钟内无效。

另一种方法是实现一个servlet会话侦听器,它在用户 – >会话映射中记录所有登录会话,并在必要时使它们无效

您可以查看有关如何连接bean的参考手册http://docs.spring.io/spring-security/site/docs/3.0.x/reference/session-mgmt.html

我花了很长时间试图达到同样的目的。

最后,我想我已经解决了。 首先删除代码的LoginCommand部分,因为正如你猜测的那样,它与发起删除的用户有关 – 在你的情况下是管理员 – 而不是目标用户的会话。

然后,尝试删除此:

sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());

出于某种原因,这似乎取消了到期而没有实际停止进一步的请求被授予。 给人印象不深!

如果它不起作用 ,那么在干涉这个的中间,我有另一个想法,我没有实现。 这是为每个请求添加一个filter,并在那里检查会话到期。

因此,在您的安全XML中:

        ... 

然后是class级:

 @Component public class CustomSessionFilter extends OncePerRequestFilter { /**The session registry.*/ @Autowired private SessionRegistry sessionRegistry; @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain ) throws ServletException, IOException { //get session for the user, and if expired, do something (eg redirect) //else... filterChain.doFilter(request, response); } } 

我发现这是基于每个请求成功调用的。 希望你不需要上面的内容,因为它是一种不太优雅的方式来实现框架应该为你做的事情。