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

Comment From: TeHMoroS

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. :)

Comment From: romeoad

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!

Comment From: mygeneralelectric

@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()); }

Comment From: smitbaranwal

@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();
    }

}

Comment From: romeoad

@smitbaranwal

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

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

Comment From: smitbaranwal

@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

Comment From: romeoad

@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.

Comment From: soheilqalamkari

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