简体   繁体   中英

Spring Boot, Spring Security, session-timeout issue with session scoped Bean, @PreDestroy

Fisrt, I need to say that I'm using session scoped bean. So before session is closed the preDestroy() method is invoked

@Component
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "session")
public class MySessionBean {

    @PreDestroy
    public void preDestroy() {

        //Do Smth with using Security principal

    }
}

When I logout by using Spring Security utils everything goes fine, the preDestroy() method is called.

The main problems come when I use

server.session-timeout = 60 or = 1 in application.properties

  1. preDestroy() is called approximately in 2.5 minutes after session has opened.
  2. And much more interesting is that SecurityContextHolder.getContext().getAuthentication().getPrincipal(); is null .
    BUT I've successfully loged out.

Also I've tried a

@Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
    return (ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) -> 
        configurableEmbeddedServletContainer.setSessionTimeout(1, TimeUnit.MINUTES);
    }

I have the same result.
Also the problem exists while using Provided Tomcat

UPDATE:

The weird thing is that if I manually after 1 minute check the session existence the method preDestroy() is called immediately. But Security Principal is already null


Thanks in Advance!

When a session does timeout the SecurityContextHolder.getContext().getAuthentication().getPrincipal() will always return null . The SecurityContext is only filled when a request comes in, one of the filters does that. When a session times out the filters will of course not be invoked and as such the SecurityContext not filled.

Instead create a bean that implements ApplicationListener<HttpSessionDestroyedEvent> . The HttpSessionDestroyedEvent has a method getSecurityContexts that returns the SecurityContext s as originally in the HttpSession .

public class YourListener implements ApplicationListener<HttpSessionDestroyedEvent> {

    public void onApplicationEvent(HttpSessionDestroyedEvent evt) {
        for (SecurityContext ctx : evt.getSecurityContexts() ) {
             Authentication auth = ctx.getAuthentication();
             Object principal = auth.getPrincipal();
             // Do your thing with the principal.
        }
    }
}

As M. Deinum said:

There is a thread which check about every x seconds if sessions are invalid. So when your set your timeout to 1 minute it is 1 minute + a bit more before your sessions is actually cleared. When you check the session yourself, the invalid session is already cleaned as then it it is forcefully checked.

So delay of preDestroy() invocation has been explained.

The next problem was how to get Security Principal after SESSION-TIMEOUT

NOTE that by implementing

  • ApplicationListener<HttpSessionDestroyedEvent>
  • HttpSessionListener
  • Session scope bean

    the

SecurityContextHolder.getContext().getAuthentication() == null when appropriate destroy method is called

To get principal visit relatedStackPost

After you'll do that implement HttpSessionListener

@Component
public class MySessionListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        ...
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        HttpSession httpSession = httpSessionEvent.getSession();
        SecurityContext securityContext = (SecurityContextImpl) httpSession.getAttribute("SPRING_SECURITY_CONTEXT");

    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM