首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用于Spring Security和/或Spring BlazeDS集成的集中式会话管理(和终止)系统

用于Spring Security和/或Spring BlazeDS集成的集中式会话管理(和终止)系统
EN

Stack Overflow用户
提问于 2013-10-07 22:20:34
回答 2查看 1.9K关注 0票数 3

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

基本上问题是: Spring Security和/或Spring BlazeDS集成是否提供了开箱即用的集中式会话管理(和终止)系统?

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

代码语言:javascript
复制
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

EN

回答 2

Stack Overflow用户

发布于 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的参考手册。

票数 0
EN

Stack Overflow用户

发布于 2013-11-29 19:40:32

我花了很长时间来尝试实现同样的目标。

最后,我想我已经解决了这个问题。首先删除代码的LoginCommand部分,因为正如您猜测的那样,它与发起删除的用户相关-在您的例子中是管理员-而不是目标用户的会话。

然后,尝试删除此代码:

sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());

由于某些原因,这似乎抵消了过期,而实际上并没有停止进一步的请求被批准。平淡无奇!

如果它不起作用,,那么在干预的过程中,我有了另一个想法,但我没有实现。这是为每个请求添加一个过滤器,并检查其中的会话过期。

因此,在您的安全XML中:

代码语言:javascript
复制
<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>
...

然后是这个类:

代码语言:javascript
复制
@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);
}
}

我发现在每次请求的基础上都能成功地调用它。希望你不需要上面的东西,因为这是一种不优雅的方式来实现框架应该为你做的事情。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19227285

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档