2013-10-07 16 views
2

我很難實現我們客戶請求的功能。簡而言之,他們希望能夠通過管理員端從應用程序中註銷他們選擇的任何客戶。該應用程序使用Flex作爲前端技術並通過AMF訪問服務器。服務器端使用Spring Security和Spring BlazeDS集成。針對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()直接失效,但似乎我無法訪問所有會話對象。

什麼是最快或最聰明的方式來實現這種功能?

最好的問候, 尤卡

回答

0

我的做法是有間接的會話無效。

使用ConcurrentSessionControl安全選項可限制每個用戶1個會話。 編寫一個自定義SessionAuthenticationStrategy,用於檢查用戶是否已被標記並在需要時使會話失效。 請注意,會話策略應在例如usernamepassword過濾器創建新會話之前執行。

您可以使用數據庫或類似靜態類的東西來保存用戶名。 此外,你可能也會想要某種時間戳,這些時間戳只會在標記後的x分鐘內使會話無效。

整整另一種方法是實現一個servlet會話監聽器,它記錄在用戶>會話映射所有登錄會話,並在必要時

你可以看看參考手冊中對如何能夠使它們無效電線上的豆 http://docs.spring.io/spring-security/site/docs/3.0.x/reference/session-mgmt.html

+0

除非我誤會,這不起作用,因爲'SessionAuthenticationStrategy'在登錄時只使用,不上後續請求。 –

0

我花了很長時間試圖達到同樣的事情。

最後,我想我已經解決了它。首先刪除代碼的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); 
} 
} 

我發現這是成功地叫上每個請求的基礎。希望你不需要上述內容,因爲這是一個不合理的方式來實現框架應該爲你做的事情。