I am using a scheduled service in spring boot app , i need to get the current connected user inside that service , my problem is that
SecurityContextHolder.getContext().getAuthentication()
returns the current connected user only once ( just after i am logged in ) , but in the next running tasks
SecurityContextHolder.getContext().getAuthentication()
returns NPE , i have searched and found that SecurityContextHolder is not shared outside the main thread.
My Service :
@Service
@EnableScheduling
public class SsePushNotificationService {
public void addEmitter(final SseEmitter emitter) {
    emitters.add(emitter);
}
public void removeEmitter(final SseEmitter emitter) {
    emitters.remove(emitter);
}
@Async("taskExecutor")
@Scheduled(fixedDelay = 5000)
public void doNotify() throws IOException {
    System.out.println("------@@@@@ inside doNotify");
    System.out.println("##### ---- curent thread /notification : " + Thread.currentThread().getName());
    if (SecurityContextHolder.getContext().getAuthentication() != null) {
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (principal instanceof UserDetails) {
            String username = ((UserDetails) principal).getUsername();
            System.out.println("------@@@@@  principal instanceof UserDetails : " + username);
        } else {
            String username = principal.toString();
            System.out.println("------@@@@@  principal : " + username);
        }
    }
}
}
the controller :
@Controller
@CrossOrigin(origins = "*")
public class SsePushNotificationRestController {
@Autowired
SsePushNotificationService service;
@Autowired
UserDetailsServiceImpl userService;
@Autowired
UserNotificationService userNotifService;
final List<SseEmitter> emitters = new CopyOnWriteArrayList<>();
String username;
int nbrEvent;
@GetMapping(value = "/notification", produces = { MediaType.TEXT_EVENT_STREAM_VALUE })
public ResponseEntity<SseEmitter> doNotify() throws InterruptedException, IOException {
    
    System.out.println("##### ---- curent thread /notification : " + Thread.currentThread().getName());
    final SseEmitter emitter = new SseEmitter();
    service.addEmitter(emitter);
    service.doNotify();
    emitter.onCompletion(() -> service.removeEmitter(emitter));
    emitter.onTimeout(() -> service.removeEmitter(emitter));
    return new ResponseEntity<>(emitter, HttpStatus.OK);
}
}
Javascript :
 const eventSource = new EventSource('http://localhost:8080/notification');
        eventSource.onmessage = e => {
            const msg = e.data;
            $("#notifCounter").text(msg);
            $("#usrNotifCounter").text(msg);
        };
        eventSource.onopen = e => console.log('open');
        eventSource.onerror = e => {
            if (e.readyState == EventSource.CLOSED) {
                console.log('close');
            }
            else {
                console.log(e);
            }
        };
        eventSource.addEventListener('second', function(e) {
            console.log('second', e.data);
        }, false);
WebSecurityConfig :
@Configuration
@EnableWebSecurity
public class WebSecurityConfig<S extends Session> extends WebSecurityConfigurerAdapter {
@Autowired
private FindByIndexNameSessionRepository<S> sessionRepository;
@Autowired
private MySessionExpiredStrategy sessionExpiredStrategy;
@Bean
public UserDetailsService userDetailsService() {
    return new UserDetailsServiceImpl();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
    DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
    authProvider.setUserDetailsService(userDetailsService());
    authProvider.setPasswordEncoder(passwordEncoder());
    return authProvider;
}
@Component
public class MySessionExpiredStrategy implements SessionInformationExpiredStrategy {
    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event)
            throws IOException, ServletException {
        HttpServletResponse response = event.getResponse();
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(
                "Your account has been logged in elsewhere, and the current login has expired. If the password is leaked, please change it immediately!");
    }
}
@Bean
public SpringSessionBackedSessionRegistry<S> sessionRegistry() {
    return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
}
@Bean
public ConcurrentSessionControlAuthenticationStrategy sessionControlAuthenticationStrategy() {
    ConcurrentSessionControlAuthenticationStrategy csas = new ConcurrentSessionControlAuthenticationStrategy(
            sessionRegistry());
    csas.setExceptionIfMaximumExceeded(true);
    return csas;
}
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/img/**", "/error");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
            .anyRequest().access("@rbacService.hasPermission(request,authentication)")
            .and().formLogin().loginPage("/login").defaultSuccessUrl("/", true).permitAll().and().logout()
            .deleteCookies("JSESSIONID").invalidateHttpSession(true).clearAuthentication(true)
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login?logout")
            .permitAll().and().exceptionHandling().accessDeniedPage("/static/403")
            .and().sessionManagement().sessionFixation().migrateSession()
            .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            .invalidSessionUrl("/static/invalidSession.html").maximumSessions(2).maxSessionsPreventsLogin(false)
            .expiredSessionStrategy(sessionExpiredStrategy).sessionRegistry(sessionRegistry())
            .expiredUrl("/login?invalid-session=true");
}
}
what is the best approch to share SecurityContextHolder between threads in that case.