简体   繁体   English

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

[英]Centralized system for session management (and killing) for Spring Security and/or Spring BlazeDS Integration

I'm having a hard time implementing a feature that our customer requests. 我很难实施客户要求的功能。 In short they want to be able to logout any customer of their choosing out of the application via the admin side. 简而言之,他们希望能够通过管理端注销选择其应用程序的任何客户。 The application is using Flex as a front end technology and accessing server via AMF. 该应用程序将Flex用作前端技术,并通过AMF访问服务器。 Server side is using Spring Security and Spring BlazeDS Integration. 服务器端正在使用Spring Security和Spring BlazeDS集成。

Basically the question is: does Spring Security and/or Spring BlazeDS Integration offer any centralized system for session management (and killing) out-of-the-box? 基本上,问题是:Spring Security和/或Spring BlazeDS Integration是否提供开箱即用的集中式系统来进行会话管理(和终止)?

For proof-of-concept purposes I have tried to logout all users and kill all sessions with following code: 为了进行概念验证,我尝试使用以下代码注销所有用户并终止所有会话:

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");
        }
    }
}

Unfortunately the above code does not work. 不幸的是,以上代码无法正常工作。 As far as I can tell this is because two things: 据我所知这是因为两件事:

A) LoginCommand is not "application wide" but tied to the current session, therefore it will try to logout only current session (the session the admin is using) and is oblivious of other sessions A)LoginCommand不是“应用程序范围”的,而是与当前会话绑定的,因此它将尝试仅注销当前会话(管理员正在使用的会话),而忽略其他会话

B) sessionInformation.expireNow() tries to expire the session but if user manages to make a request before session gets invalidated, the session is not destroyed B)sessionInformation.expireNow()尝试使会话过期,但是如果用户设法在会话无效之前发出请求,则会话不会被破坏

From the documentation I can see that session could be directly invalidated by session.invalidate(), but it seems I have no way to access all session objects. 从文档中,我可以看到session.invalidate()可以直接使会话无效,但是看来我无法访问所有会话对象。

What is the fastest or smartest way to implement this kind of feature? 实现此类功能的最快或最明智的方法是什么?

Best regards, Jukka 最好的问候,Jukka

My approach would be to have an indirect session invalidation. 我的方法是使间接会话无效。

Use the ConcurrentSessionControl security option to limit 1 session per user. 使用ConcurrentSessionControl安全选项可以限制每个用户1个会话。 Write a custom SessionAuthenticationStrategy which checks if a user has been marked and invalidates the session if needed. 编写一个自定义的SessionAuthenticationStrategy,以检查是否已标记用户并在需要时使会话无效。 Note that the session strategy should be executed before for example the usernamepassword filter creates a new session. 请注意,会话策略应在例如usernamepassword过滤器创建新会话之前执行。

You can user either a database or something like a static class to hold the usernames. 您可以使用数据库或类似静态类的名称来保存用户名。 Furthermore you probably you also would want to have some kind of timestamp, which only invalidats sessions x minutes after they have been marked. 此外,您可能还希望具有某种时间戳记,仅在会话被标记后的x分钟内使会话无效。

A whole another approach is to implement a servlet session listener, which records all login sessions in a user->session map and can invalidate them if necessary 另一种方法是实现Servlet会话侦听器,该侦听器在用户->会话映射中记录所有登录会话,并在必要时可以使它们无效

You can take a look at the reference manual on how to wire up the beans http://docs.spring.io/spring-security/site/docs/3.0.x/reference/session-mgmt.html 您可以查看有关如何连接Bean的参考手册http://docs.spring.io/spring-security/site/docs/3.0.x/reference/session-mgmt.html

I've spent a long time trying to achieve the same thing. 我花了很长时间尝试实现相同的目标。

In the end, I think I've resolved it. 最后,我想我已经解决了。 First remove the LoginCommand section of your code, because as you surmise, it relates to the user initiating the delete - in your case, the admin - and not the session of the target user. 首先删除代码中的LoginCommand部分,因为您推测它与启动删除的用户有关(在本例中为admin),而不是目标用户的会话。

Then, try removing this: 然后,尝试删除此内容:

sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());

For some reason this appears to cancel out the expiry without actually stopping further requests being granted. 由于某种原因,这似乎取消了有效期,而实际上没有停止进一步的请求被授予。 Unimpressive! 令人印象深刻!

If it doesn't work , then in the midsts of meddling with this, I had another idea which I didn't bring to fruition. 如果它不起作用 ,那么在进行干预的过程中,我有另一个想法没有实现。 This is to add a filter for every request, and check for session expiry in there. 这是为每个请求添加一个过滤器,并在其中检查会话到期。

So, in your security XML: 因此,在您的安全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>
...

and then the 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 (e.g. redirect)

    //else...
    filterChain.doFilter(request, response);
}
}

I found this to be successfully called on a per-request basis. 我发现可以根据每个请求成功调用它。 Hopefully you don't need the above, as it's an inelegant way to achieve something the framework ought to be doing for you. 希望您不需要上面的内容,因为这是实现框架应为您做的事情的一种巧妙方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM