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()?