2017-08-25 48 views
1

我試圖在我的Spring Boot應用程序中實現「強制註銷」功能(例如,管理員禁用用戶帳戶後)。Spring引導會話管理 - 爲什麼會有兩個sessionRegistry實例?

我按照各種教程中指定的步驟訪問會話註冊表,以便過期用戶的會話(baeldung step 6verbose version on myyurt;還有related SO)。

然而,註冊SessionRegistryImpl作爲@Bean後,使用調試器時,我看到有在依賴噴射機構2個的不同實例:

  • 一個的SessionRegistry實例在登錄時使用的春季安全並按預期登出並擔任主席和會議。下面的截圖是在登錄後進行的 - 我在registerNewSession()方法中有一個斷點。注意已經登錄用戶的id和地圖。 registerNewSession() happens at login

  • 另一個sessionRegistry實例只被給予我自己的SessionManager類,它需要SessionRegistry作爲依賴並調用getAllPrincipals()。注意ID是不同的,地圖是空的(I稱爲getAllPrincipals()我登錄多次後,把所述第一屏幕截圖) getAllPrincipals() query from my own class

類其中I註冊的SessionRegistry豆(I除去不必要的代碼只留下我的自定義過濾器,在情況下,它可能是與泉自動配置):

@EnableWebSecurity 
class SecurityConfig extends WebSecurityConfigurerAdapter { 

    @Bean 
    public static HttpSessionEventPublisher httpSessionEventPublisher() { 
     return new HttpSessionEventPublisher(); 
    } 

    @Bean 
    public static SessionRegistry sessionRegistry() { 
     return new SessionRegistryImpl(); 
    } 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 

     http 
      .addFilterBefore(myFirstFilter, UsernamePasswordAuthenticationFilter.class) 
      .addFilterAfter(mySecondFilter, FilterSecurityInterceptor.class) 
      .formLogin() // skipping details 
      .and() 
      .x509() // skipping details 
      .and() 
      .logout() 
       .invalidateHttpSession(true) 
       .permitAll() 
      .and() 
      .authorizeRequests() // skipping details 
      .and() 
      .sessionManagement() 
       .invalidSessionUrl("/login") 
       .enableSessionUrlRewriting(false) 
       .maximumSessions(-1) 
        .maxSessionsPreventsLogin(false) 
        .sessionRegistry(sessionRegistry()) 
        .expiredUrl("/login?expire"); 
    }  
} 

類,我消費的SessionRegistry依賴性:

@Component 
class DefaultSessionManager implements SessionManager {  
    private final SessionRegistry sessionRegistry; 

    @Autowired public DefaultSessionManager(SessionRegistry sessionRegistry) { 
     this.sessionRegistry = sessionRegistry; 
    } 

    public void expireUserSessions(String username) { 
     for (Object principal : sessionRegistry.getAllPrincipals()) { 
      // do stuff, but won't enter because the list is empty 
     } 
    } 
} 

如果有幫助,我擡頭一看豆設置與執行器/豆端點,而這也正是它返回

 { 
      "bean": "defaultSessionManager", 
      "aliases": 
      [ 
      ], 
      "scope": "singleton", 
      "type": "com.foo.bar.DefaultSessionManager", 
      "resource": // file path 
      "dependencies": 
      [ 
       "sessionRegistry" 
      ] 
     }, 

     { 
      "bean": "httpSessionEventPublisher", 
      "aliases": 
      [ 
      ], 
      "scope": "singleton", 
      "type": "org.springframework.security.web.session.HttpSessionEventPublisher", 
      "resource": "class path resource [com/foo/bar/SecurityConfig.class]", 
      "dependencies": 
      [ 
      ] 
     }, 
     { 
      "bean": "sessionRegistry", 
      "aliases": 
      [ 
      ], 
      "scope": "singleton", 
      "type": "org.springframework.security.core.session.SessionRegistryImpl", 
      "resource": "class path resource [com/foo/bar/SecurityConfig.class]", 
      "dependencies": 
      [ 
      ] 
     }, 

哪有在DI系統中的兩個不同的實例,如果所有被聲明爲辛格爾頓? 你有什麼提示可能是錯誤的?

我正在使用spring-boot-starter-parent 1.5.2.RELEASE,它使用Spring Security 4.2.2.RELEASE。

+0

我不知道它是否相關,但用'@ Bean'註釋的方法不需要是'static'。由於Spring在配置類中使用代理等,這可能是一個探索,但我不確定。也許嘗試刪除靜態關鍵字。 – dunni

+0

我在春天遇到過這樣的問題。你能顯示引用SessionRegistries的對象嗎?在我的情況下,應用程序中有上下文。 – egorlitvinenko

+0

實際上我認爲靜態關鍵字是問題,因爲在你調用'.sessionRegistry(sessionRegistry())'的配置中,它直接調用靜態方法而不是通過Spring代理來獲取應用程序上下文中的bean 。這意味着,爲了您的安全配置,您創建了一個新實例,而不是從應用程序上下文中獲取該bean。 – dunni

回答

1

問題是您的@Bean帶註釋的方法的靜態關鍵字。在您的配置,你叫

.sessionRegistry(sessionRegistry()) 

它直接調用靜態方法,而不是通過Spring代理等,將得到從應用程序上下文豆去。這意味着,爲了您的安全配置,您創建了一個新實例,而不是從應用程序上下文中獲取該bean。使用非靜態方法,Spring會攔截相同調用中的直接方法調用,然後檢查bean是否已經存在於應用程序上下文中,如果是,則返回該bean。

+0

謝謝,[docs](https://stackoverflow.com/a/14943106/2261315)對這一個不是很有幫助。但我會銘記未來。那裏發生了很多魔術。 –

+2

一般的經驗法則(沒有官方的,只是從我身邊):靜態和春天通常不會在一起玩。 – dunni