Spring-security: SEC-2855: SessionManagementConfigurer does not allow SessionRegistry to receive events

Created on 17 Feb 2015  路  8Comments  路  Source: spring-projects/spring-security

Rob Winch (Migrated from SEC-2855) said:

The SessionRegistry is not exposed as a Bean so there is no way for ApplicationEvent to be published to it. A workaround is to do something like:

@EnableWebMvcSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                    .antMatchers("/expired").permitAll()
                    .anyRequest().authenticated()
                    .and()
                .formLogin()
                    .and()
                .sessionManagement()
                    .maximumSessions(1)
                    .expiredUrl("/expired")
                    .maxSessionsPreventsLogin(true)
                    .sessionRegistry(sessionRegistry());
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        SessionRegistry sessionRegistry = new SessionRegistryImpl();
        return sessionRegistry;
    }
}
config bug jira

Most helpful comment

Since I was using custom login success/failure and logout success handlers, none of above solutions was working properly, and I ended up with this solution:

@Configuration
@EnableWebMvcSecurity
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/error").permitAll()
                .antMatchers("/public/api/**").permitAll()
                .antMatchers("/api/*/**").hasAnyAuthority(/* authorities list here */)
                /* more security related config cut */
                .anyRequest().authenticated()
                .formLogin().loginPage("/login.html").permitAll().loginProcessingUrl("/login").permitAll()
                .and().formLogin().successHandler(authSuccessHandler())
                .and().formLogin().failureHandler(authFailureHandler())
                .and().logout().permitAll().logoutSuccessHandler(appLogoutSuccessHandler())
                .and().sessionManagement()
                    .maximumSessions(1)
                    .maxSessionsPreventsLogin(true)
                    .expiredUrl("/login.html?expired")
                    .sessionRegistry(sessionRegistry());
    }

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

    @Bean
    protected AuthenticationSuccessHandler authSuccessHandler() {
        return new LoginSuccesHandler();
    }

    @Bean
    protected AuthenticationFailureHandler authFailureHandler() {
        return new LoginFailureHandler();
    }

    @Bean
    protected LogoutSuccessHandler appLogoutSuccessHandler() {
        return new AppLogoutSuccessHandler();
    }
}

logout success handler:

public class AppLogoutSuccessHandler implements LogoutSuccessHandler {
    @Autowired
    private SessionRegistry sessionRegistry;

    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Authentication authentication)
            throws IOException, ServletException {
        if (authentication != null && authentication.getDetails() != null) {
            removeAuthSession(authentication, sessionRegistry);
            httpServletRequest.getSession().invalidate();
            httpServletResponse.sendRedirect("login.html?logout");
        }
    }

    private void removeAuthSession(Authentication authentication, SessionRegistry sessionRegistry) {
        List<SessionInformation> sessions = sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
        if (sessions.size() > 0) { // there is only 1 session allowed
            log.debug("removing session {} from registry", sessions.get(0).getSessionId());
            sessionRegistry.removeSessionInformation(sessions.get(0).getSessionId());
        }
    }
}

Hope this helps somebody!

All 8 comments

Hi. I've kind of stumbled on a similar problem, when Spring Security wasn't notified that a session has expired (either through timeout of a logout() configuration). I don't know if this will help in your case (a lot of people use Spring Boot, I don't, not for this particular project at least), but what worked for me was customizing my security initializer.

public class WebSecurityEntryPoint extends AbstractSecurityWebApplicationInitializer {

    @Override
    protected boolean enableHttpSessionEventPublisher() {
        // this method changed everything for me
        return true;
    }
}

My security config is similar to yours, with minimal differences:

  • no sessionRegistry workaround (that's why I posted in the first place);
  • no expireUrl - just redirecting to login page and handling everything via status codes (a single-page app);
  • additional config: sessionFixation (migrate) and disabling of url rewriting (cookie only session IDs).

What worries me the most is the amount of digging to do, to find this (in JavaDocs, as I found it by accident). Every solution I found was talking about registering the HttpSessionEventPublisher as a bean in my java config (which did not work) or registering it manually in the ServletContext (which I thought was a bit out-of-place given that everything else in Spring, Web MVC and Security was working well without that kind of manual overrides). Only after searching for HttpSessionEventPublisher I stumbled upon the little method mentioned above (Google's search results text highlighting FTW! :P ) and decided to try it (without any tricks or bean registration). It worked. :)

Since I was using custom login success/failure and logout success handlers, none of above solutions was working properly, and I ended up with this solution:

@Configuration
@EnableWebMvcSecurity
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/error").permitAll()
                .antMatchers("/public/api/**").permitAll()
                .antMatchers("/api/*/**").hasAnyAuthority(/* authorities list here */)
                /* more security related config cut */
                .anyRequest().authenticated()
                .formLogin().loginPage("/login.html").permitAll().loginProcessingUrl("/login").permitAll()
                .and().formLogin().successHandler(authSuccessHandler())
                .and().formLogin().failureHandler(authFailureHandler())
                .and().logout().permitAll().logoutSuccessHandler(appLogoutSuccessHandler())
                .and().sessionManagement()
                    .maximumSessions(1)
                    .maxSessionsPreventsLogin(true)
                    .expiredUrl("/login.html?expired")
                    .sessionRegistry(sessionRegistry());
    }

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

    @Bean
    protected AuthenticationSuccessHandler authSuccessHandler() {
        return new LoginSuccesHandler();
    }

    @Bean
    protected AuthenticationFailureHandler authFailureHandler() {
        return new LoginFailureHandler();
    }

    @Bean
    protected LogoutSuccessHandler appLogoutSuccessHandler() {
        return new AppLogoutSuccessHandler();
    }
}

logout success handler:

public class AppLogoutSuccessHandler implements LogoutSuccessHandler {
    @Autowired
    private SessionRegistry sessionRegistry;

    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Authentication authentication)
            throws IOException, ServletException {
        if (authentication != null && authentication.getDetails() != null) {
            removeAuthSession(authentication, sessionRegistry);
            httpServletRequest.getSession().invalidate();
            httpServletResponse.sendRedirect("login.html?logout");
        }
    }

    private void removeAuthSession(Authentication authentication, SessionRegistry sessionRegistry) {
        List<SessionInformation> sessions = sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
        if (sessions.size() > 0) { // there is only 1 session allowed
            log.debug("removing session {} from registry", sessions.get(0).getSessionId());
            sessionRegistry.removeSessionInformation(sessions.get(0).getSessionId());
        }
    }
}

Hope this helps somebody!

@TeHMoroS @romeoad
I have tried both of your solutions,but they didn't work

I using Spring Security 4.0.4

This is my properties
<properties> <springframework.version>4.2.5.RELEASE</springframework.version> <springsecurity.version>4.0.4.RELEASE</springsecurity.version> <hibernate.version>4.3.11.Final</hibernate.version> <mysql.connector.version>5.1.31</mysql.connector.version> </properties>

And this is my main configuration
protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/", "/info").access("hasRole('REGULAR') or hasRole('GROUP') or hasRole('AREA') or hasRole('ADMIN')") .and().formLogin().loginPage("/login") .loginProcessingUrl("/login").usernameParameter("jobId").passwordParameter("password") .defaultSuccessUrl("/info", false) .and().logout() .logoutUrl("/logout") .logoutSuccessUrl("/login?logout") .invalidateHttpSession(true) .deleteCookies("JSESSIONID") .and().rememberMe().rememberMeParameter("remember-me").tokenRepository(tokenRepository) .tokenValiditySeconds(3600) .and().csrf() .and().exceptionHandling().accessDeniedPage("/Access_Denied") .and().sessionManagement() .sessionFixation().changeSessionId() .maximumSessions(1).maxSessionsPreventsLogin(true) .sessionRegistry(sessionRegistry()); }

@romeoad
this success handler is not working for me

`@Configuration
@EnableWebMvcSecurity
@ComponentScan(basePackageClasses = MyUserDetailService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

@Autowired
private UserDetailsService userDetailsService;


@Bean(name="passwordEncoder")
public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder(); 
}

@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception{
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

 @Override
 protected void configure(HttpSecurity http) throws Exception {
   http.authorizeRequests()
  .antMatchers("/hello").access("hasRole('ROLE_ADMIN')")
  .anyRequest().permitAll()
  .and()
    .formLogin().loginPage("/login")
    .usernameParameter("username").passwordParameter("password")
  .and()
    .logout().logoutSuccessUrl("/login?logout") 
   .and()
   .exceptionHandling().accessDeniedPage("/403")
  .and()
    .csrf();
 }

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


 @Bean
    protected LogoutSuccessHandler appLogoutSuccessHandler() {
        return new AppLogoutSuccessHandler();
    }

}

@smitbaranwal

And where is your LogoutSuccessHandler wired to logout action? Like this:

[...]
.and().logout().permitAll().logoutSuccessHandler(appLogoutSuccessHandler())
[...]

@romeoad
Yeah it is working fine but still there is one problem, that when i try to get logged out :
List<SessionInformation> sessions = sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
i am getting sessions size as 0

@smitbaranwal
My example is almost 2 years old now, so something could have changed.

If there is no session information one can assume that user is logged out I suppose. Maybe session is destroyed in previous parts of lifecycle.

hi every body!How to get List of logged in user in spring security oauth?Is it same use sessionRegistry.getAllPrincipals()?

Was this page helpful?
0 / 5 - 0 ratings