4

Is @Scheduled annotated method out of any SecurityContext? When I try to use the following it always has securitycontext/auth as null. If this is not possible then what could be the right way to run user specific authentication aware scheduled task?

    @Scheduled(fixedDelay = 10000)
    // This method will send notifications to the current user
    public void sendUserNotifications() {
        SecurityContext sc = SecurityContextHolder.getContext();
        Authentication auth = sc.getAuthentication();

        if(auth == null) {

            log.info(">> SecurityContext=[{}], Authentication[auth] is {}, please login to receive notifications", sc, auth);

            return;
        }
muasif80
  • 5,586
  • 4
  • 32
  • 45

1 Answers1

0

Given excerpt will never work.

Explanation:

Native session management is provided by underlying servlet API and container. HttpSession object is only created when the user logs in using request.getSession() under the hood and later this HttpSession will be used by SecurityContextHolder.getContext().getAuthentication(); internally. This means it has a relation with the user's request which is a container-managed thread, not plain old JVM thread on which your scheduler will run.

Here your scheduler does not have any dependency on the user it will run independently on JVM managed thread. So no HttpRequest/HttpSession object will be created at all.

Imagine the very first time when you start the application and no user yet logged in then what would be scenario here.

So you will always get securitycontext/auth as null only.

To answer your question

If this is not possible then what could be the right way to run user specific authentication aware scheduled task?

One way I can think of right now is, Use of spring provided SessionRegistry. It keeps track of all currently logged-in users.

So you can pass this SessionRegistry object by autowiring to this scheduler and get list of all principal/logged-in users and send them a notification.

Something like below -

    @EnableScheduling
    @Component
    public class MyScheduler { 

    @Autowired
    @Qualifier("sessionRegistry")
    private SessionRegistry sessionRegistry;
    @Scheduled(fixedDelay = 10000)
    // This method will send notifications to the current user
    public void sendUserNotifications() {
    List<UserDetails> principals = sessionRegistry.getAllPrincipals()
        .stream()
        .filter(principal -> principal instanceof UserDetails)
        .map(UserDetails.class::cast)
        .collect(Collectors.toList());

    // send notification to all users.      
   }

Also, you have to enable sessionRegistry in your security config first you have to listen to HTTP sessions and then configure registry in your security config.

        public class AppInitializer implements WebApplicationInitializer {

        @Override
        public void onStartup(ServletContext servletContext) {
        ...
        servletContext.addListener(HttpSessionEventPublisher.class);
         }
        }

and security config -

 @Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        // ...
        http.sessionManagement().maxSession(1).sessionRegistry(sessionRegistry());
    }

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

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

For more details of how to get current logged in users see here.

Rajeev
  • 1,730
  • 3
  • 20
  • 33