我很难实现我们客户所要求的功能。简而言之,他们希望能够通过管理员端注销他们选择退出应用程序的任何客户。该应用程序使用Flex作为前端技术,并通过AMF访问服务器。服务器端使用Spring Security和Spring BlazeDS集成。
基本上问题是: Spring Security和/或Spring BlazeDS集成是否提供了开箱即用的集中式会话管理(和终止)系统?
出于概念验证的目的,我尝试注销所有用户,并使用以下代码终止所有会话:
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<Object> 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<SessionInformation> 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()可以直接使会话失效,但是我似乎没有办法访问所有的会话对象。
实现这种功能的最快或最智能的方式是什么?
最好的问候,Jukka
发布于 2013-10-08 00:19:56
我的方法是使用间接会话失效。
使用ConcurrentSessionControl安全选项限制每个用户一个会话。编写一个自定义SessionAuthenticationStrategy,它检查用户是否已被标记,并在需要时使会话无效。注意,会话策略应该在例如usernamepassword过滤器创建新会话之前执行。
您可以使用数据库或像静态类这样的东西来保存用户名。此外,您可能还希望有某种类型的时间戳,它仅在会话被标记后x分钟才会使会话无效。
另一种方法是实现servlet会话侦听器,它在user->会话映射中记录所有登录会话,并在必要时使它们无效。
您可以查看有关如何连接bean http://docs.spring.io/spring-security/site/docs/3.0.x/reference/session-mgmt.html的参考手册。
发布于 2013-11-29 19:40:32
我花了很长时间来尝试实现同样的目标。
最后,我想我已经解决了这个问题。首先删除代码的LoginCommand部分,因为正如您猜测的那样,它与发起删除的用户相关-在您的例子中是管理员-而不是目标用户的会话。
然后,尝试删除此代码:
sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());
由于某些原因,这似乎抵消了过期,而实际上并没有停止进一步的请求被批准。平淡无奇!
如果它不起作用,,那么在干预的过程中,我有了另一个想法,但我没有实现。这是为每个请求添加一个过滤器,并检查其中的会话过期。
因此,在您的安全XML中:
<beans:bean id="sessionFilter" class="my.package.CustomSessionFilter"/>
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
<http use-expressions="true" auto-config="true">
<custom-filter after="CONCURRENT_SESSION_FILTER" ref="sessionFilter"/>
<session-management>
<concurrency-control session-registry-ref="sessionRegistry"/>
</session-management>
...然后是这个类:
@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 (e.g. redirect)
//else...
filterChain.doFilter(request, response);
}
}我发现在每次请求的基础上都能成功地调用它。希望你不需要上面的东西,因为这是一种不优雅的方式来实现框架应该为你做的事情。
https://stackoverflow.com/questions/19227285
复制相似问题