Spring From the Trenches: Invoking a Secured Method From a Scheduled Job

Let's assume that we have implemented a Spring powered application, and secured it by using the method security expressions of Spring Security.

Our next task is to implement a scheduled job which uses the secured methods. To be more specific, we have to implement a scheduled job which obtains a message from our service class and writes the received message to the log.

Let's get started.

The scheduled jobs described in this blog post use cron expressions which are configured in the profile specific configuration files. If you don't know how you can do this, I recommend that you read my blog post which describes how you can use environment specific cron expressions with the @Scheduled annotation.

Our First Attempt

Let's create a scheduled job which invokes the secured method and find out what happens when the job is executed. Let's start by taking a look at the service layer of our example application.

The Service Layer

The methods of the secured service class are declared in the MessageService interface. It declares one method called getMessage() and specifies that only users who have a role ROLE_USER can invoke it.

The source code of the MessageService interface looks as follows:

import org.springframework.security.access.prepost.PreAuthorize;

public interface MessageService {

    @PreAuthorize("hasRole('ROLE_USER')")
    public String getMessage();
}

Our implementation of the MessageService interface is rather simple. Its source code looks as follows:

import org.springframework.stereotype.Service;

@Service
public class HelloMessageService implements MessageService {

    @Override
    public String getMessage() {
        return "Hello World!";
    }
}

Let's move on and create scheduled job which invokes the getMessage() method.

Creating the Scheduled Job

We can create the scheduled job by following these steps:

  1. Create a ScheduledJob class and annotate it with the @Component annotation. This ensures that our scheduled job is found during the classpath scan (as long as we put it to a package which is scanned).
  2. Add a private Logger field to the created class and create a Logger object by calling the static getLogger() method of the LoggerFactory class. We will use the Logger object to write the message which we receive from the HelloMessageService object to the log.
  3. Add a private MessageService field to created class.
  4. Add a constructor to the created class and annotate it with the @Autowired annotation. This ensures that we can inject a MessageService bean to the MessageService field by using constructor injection.
  5. Add a public run() method to created class and annotate it with the @Scheduled annotation. Set the value of its cron attribute to '${scheduling.job.cron}'. That means that the cron expression is read from a properties file, and its value is the value of the scheduling.job.cron property (See this blog post for more details about this).
  6. Implement the run() method by calling the getMessage() method of the MessageService interface. Write the received message to the log.

The source code of our scheduled job looks as follows:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledJob {

    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledJob.class);

    private final MessageService messageService;

    @Autowired
    public ScheduledJob(MessageService messageService) {
        this.messageService = messageService;
    }

    @Scheduled(cron = "${scheduling.job.cron}")
    public void run() {
        String message = messageService.getMessage();
        LOGGER.debug("Received message: {}", message);
    }
}

Let's see what happens when the run() method of the ScheduledJob class is invoked.

It Doesn't Work

When our scheduled job is executed, the AuthenticationCredentialsNotFoundException is thrown and we see the following stacktrace:

2013-12-10 19:45:19,001 ERROR - kUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task.
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:339)
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:198)
	at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:60)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
	at com.sun.proxy.$Proxy31.getMessage(Unknown Source)
	at net.petrikainulainen.spring.trenches.scheduling.job.ScheduledJobTwo.run(ScheduledJobTwo.java:26)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64)
	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)
	at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
	at java.util.concurrent.FutureTask.run(FutureTask.java:166)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:722)

That stacktrace is actually pretty helpful. It tells us that the secured method couldn't be invoked because an Authentication object was not found from the SecurityContext.

The two most common solutions to this problem which I have seen are:

  • Create a separate method which does the same thing than the protected method, and modify the scheduled job to use this method. This method often has a Javadoc comment which states that the only the scheduled job can call this method. This solution has two problems: 1) it clutters the codebase and 2) someone will eventually call that method anyway (no one really read Javadocs unless they have to).
  • Remove the method security annotation from the method invoked by the scheduled job. This is a really poor solution for obvious reasons. Hint: That method was secured for a good reason!

Luckily, there is also a third way to solve this problem. Let's start by finding out where the security context used by our scheduled job is stored.

Where Does the Security Context Come From?

The solution of our problem is clear:

We have to create an Authentication object and add it to the SecurityContext before the secured method is invoked.

However, before we can make the necessary modifications to our example application, we have to understand where the SecurityContext object is stored.

If we haven't configured otherwise, the security context is stored to ThreadLocal. In other words, each thread has its own security context. This means that all scheduled jobs which are executed in the same thread share the same security context.

Let's assume that we have three scheduled jobs. These jobs are called A, B, and C. Also, let’s assume that these jobs are executed in alphabetical order.

If we use the default thread pool which has only one thread, all jobs share the same security context. If the job B sets the Authentication object to the security context, the following things happen when the scheduled jobs are executed:

  • The job A cannot invoke the secured method because it is executed before job B. This means that an Authentication object is not found from the security context.
  • The job B can invoke the secured method because it sets the Authentication object to the security context before it tries to invoke the secured method.
  • The job C can invoke the secured method because it is executed after job B which sets the Authentication object to the security context.

If we use a thread pool which has more than one thread, each thread has its own security context. If the job A sets the Authentication object to the security context, all jobs which are executed in the same thread are executed by using the same privileges as long as they are executed after job A.

Let's go through each job one by one:

  • The job A can invoke the secured method because it sets the Authentication object to the security context before it tries to invoke the secured method.
  • The job B can invoke the secured method IF it is executed in the same thread than job A. If the job is isn't executed in the same thread, it cannot invoke the secured method because the Authentication object is not found from the security context.
  • The job C can invoke the secured method IF it is executed in the same thread than job A. If the job is isn't executed in the same thread, it cannot invoke the secured method because the Authentication object is not found from the security context.

It is clear that the best way to solve this problem is to ensure that each scheduled job is executed by using the required privileges. This solution has two benefits:

  • We can execute our jobs in any order.
  • We don't have to ensure that jobs are executed in a "correct" thread.

Let's find out how we can solve this problem when our application uses Spring Security 3.1.

Spring Security 3.1: Manual Work Required

If our application uses Spring Security 3.1, the easiest way to solve our problem is

  • Create an Authentication object and set it to the security context before our job tries to invoke the secured method.
  • Remove the Authentication object from the security context before the job is finished.

Let's start by creating an AuthenticationUtil class which provides the required methods.

Creating the AuthenticationUtil Class

We can create the AuthenticationUtil class by following these steps:

  1. Create the AuthenticationUtil class.
  2. Add a private constructor the AuthenticationUtil class. This ensures that the class cannot be instantiated.
  3. Add a static clearAuthentication() method to the class, and implement the method by following these steps:
    1. Get the SecurityContext object by calling the static getContext() method of the SecurityContextHolder class.
    2. Remove the authentication information by calling the setContext() method of the SecurityContext interface. Pass null as a method parameter.
  4. Add a static configureAuthentication() method to the class. This method takes the role of the user as a method parameter. Implement this method by following these steps:
    1. Create a Collection of GrantedAuthority objects by calling the static createAuthorityList() method of the AuthorityUtils class. Pass the user role as a method parameter.
    2. Create a new UsernamePasswordAuthenticationToken object and pass the following objects as constructor arguments:
      1. The first constructor argument is the principal. Pass the String 'user' as the first constructor argument.
      2. The second constructor argument is the credentials of the user. Pass the role given as a method parameter as the second constructor argument.
      3. The third constructor argument contains the authorities of the user. Pass the created Collection<GrantedAuthority> object as the third constructor argument.
    3. Get the SecurityContext object by calling the static getContext() method of the SecurityContextHolder class.
    4. Set the created Authentication object to the security context by calling the setAuthentication() method of the SecurityContext interface. Pass the created UsernamePasswordAuthenticationToken as a method parameter.

The source code of the AuthenticationUtil class looks as follows:

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.Collection;

public final class AuthenticationUtil {

    //Ensures that this class cannot be instantiated
    private AuthenticationUtil() {
    }

    public static void clearAuthentication() {
        SecurityContextHolder.getContext().setAuthentication(null);
    }

    public static void configureAuthentication(String role) {
        Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(role);
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                "user",
                role,
                authorities
        );
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }
}

We are not done yet. We still have to make some modifications to our scheduled job. Let's find out how we can make these modifications.

Modifying the Scheduled Job

We have to make two modifications to the ScheduledJob class. We can make these modifications by following these steps:

  1. Call the static configureAuthentication() method of the AuthenticationUtil class when the job is started and pass the String 'ROLE_USER' as a method parameter. This ensures that our scheduled job can execute the same methods than a regular user who has a role ROLE_USER.
  2. Call the static clearAuthentication() method of the AuthenticationUtil class just before the job is finished. This removed the authentication information from the security context.

The source code of the ScheduledJob class looks as follows:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledJob {

    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledJob.class);

    private final MessageService messageService;

    @Autowired
    public ScheduledJob(MessageService messageService) {
        this.messageService = messageService;
    }

    @Scheduled(cron = "${scheduling.job.cron}")
    public void run() {
        AuthenticationUtil.configureAuthentication("ROLE_USER");

        String message = messageService.getMessage();
        LOGGER.debug("Received message: {}", message);

        AuthenticationUtil.clearAuthentication();
    }
}
As Derek pointed out, we should invoke the clearAuthentication() method of the AuthenticationUtil class inside a finally block. If we don't do this, our security context might leak into the job's thread pool that could be shared with other jobs.

Let's find out what happens when our scheduled job is run.

Running the Scheduled Job

When the job is invoked, the following message is written to the log:

2013-12-17 20:41:33,019 DEBUG - ScheduledJob            - Received message: Hello World!

Everything is working perfectly when our application uses Spring Security 3.1. Our solution is not that elegant, but it works. The obvious drawback of this solution is that we have to remember to call the configureAuthentication() and clearAuthentication() methods of the AuthenticationUtil class in our scheduled jobs.

Spring Security 3.2 solves this problem. Let's move on and find out how we can solve this problem when our application uses Spring Security 3.2.

Spring Security 3.2: It Is Almost Like Magic!

Spring Security 3.2 has a brand new concurrency support which gives us the possibility to transfer the security context from one thread to another. Let's find out how we can configure our application context to use the features provided by Spring Security 3.2.

Configuring the Application Context

Because we want to use the new concurrency support of Spring Security 3.2, we have to make the following changes to our application context configuration class (the original configuration is described in this blog post):

  1. Implement the SchedulingConfigurer interface. This interface can be implemented by application context configuration classes which are annotated with the @EnableScheduling annotation, and it is often used to to configure the used TaskScheduler bean or configure the executed tasks programmatically.
  2. Add a private createrSchedulerSecurityContext() method to the configuration class. This method has no method parameters it returns a SecurityContext object. Implement this method by following these steps:
    1. Create a new SecurityContext object by calling the static createEmptyContext() method of the SecurityContextHolder class.
    2. Create a Collection of GrantedAuthority objects by calling the static createAuthorityList() method of the AuthorityUtils class. Pass the String 'ROLE_USER' as a method parameter.
    3. Create a new UsernamePasswordAuthenticationToken object and pass the following objects as constructor arguments:
      1. The first constructor argument is the principal. Pass the String 'user' as the first constructor argument.
      2. The second constructor argument is the credentials of the user. Pass the String 'ROLE_USER' as the second constructor argument.
      3. The third constructor argument contains the authorities of the user. Pass the created Collection<GrantedAuthority> object as the third constructor argument.
    4. Set the created UsernamePasswordAuthenticationToken object to the created security context by calling the setAuthentication() method of the SecurityContext interface.
  3. Add a public taskExecutor() method to the configuration class and annotate the method with the @Bean annotation. This method has no method parameters and returns an Executor object. Implement this method by following these steps:
    1. Create a new ScheduledExecutorService object by calling the static newSingleThreadScheduledExecutor() method of the Executors class. This creates a ScheduledExecutorService object which runs all jobs by using a single thread.
    2. Obtain a reference to the SecurityContext object by calling the private createSchedulerSecurityContext() method.
    3. Create a new DelegatingSecurityContextScheduledExecutorService object and pass the following objects as constructor arguments:
      1. The first constructor argument is a ScheduledExecutorService object. This object is used to invoke scheduled jobs. Pass the created ScheduledExecutorService object as the first constructor argument.
      2. The second constructor argument is a SecurityContext object. The created DelegatingSecurityContextScheduledExecutorService object ensures that each invoked job uses this SecurityContext. Pass the created the SecurityContext object as the second constructor argument.
    4. Return the created DelegatingSecurityContextScheduledExecutorService object.
  4. Implement the configureTasks() method of the of the SchedulingConfigurer interface. This method takes a ScheduledTaskRegistrar object as a method parameter. Implement this method by following these steps:
    1. Create a new Executor object by calling the taskExecutor() method.
    2. Set the used scheduler by calling the setScheduler() method of the ScheduledTaskRegistrar class and pass the Executor object as a method parameter.

The source code of the ExampleApplicationContext class looks as follows (the relevant parts are highlighted):

import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

@Configuration
@EnableScheduling
@ComponentScan(basePackages = {
        "net.petrikainulainen.spring.trenches.scheduling"
})
@Import(ExampleSecurityContext.class)
@PropertySource("classpath:application.properties")
public class ExampleApplicationContext implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    @Bean
    public Executor taskExecutor() {
        ScheduledExecutorService delegateExecutor = Executors.newSingleThreadScheduledExecutor();
        SecurityContext schedulerContext = createSchedulerSecurityContext();
        return new DelegatingSecurityContextScheduledExecutorService(delegateExecutor, schedulerContext);
    }

    private SecurityContext createSchedulerSecurityContext() {
        SecurityContext context = SecurityContextHolder.createEmptyContext();

        Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_USER");
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                "user",
                "ROLE_USER",
                authorities
        );
        context.setAuthentication(authentication);

        return context;
    }

    @Bean
    public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();

        properties.setLocation(new ClassPathResource( "application.properties" ));
        properties.setIgnoreResourceNotFound(false);

        return properties;
    }
}

That is it. This configuration ensures that each scheduled job has access to the SecurityContext object created by the createSchedulerSecurityContext() method. This means that each scheduled job can invoke secured methods which can be invoked by a user who has the role 'ROLE_USER'.

Let's take a quick look at our scheduled job.

What About the Scheduled Job?

The best part of this solution is that we don’t have to make any changes to the ScheduledJob class. Its source code looks as follows:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledJob {

    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledJob.class);

    private final MessageService messageService;

    @Autowired
    public ScheduledJob(MessageService messageService) {
        this.messageService = messageService;
    }

    @Scheduled(cron = "${scheduling.job.cron}")
    public void run() {
        String message = messageService.getMessage();
        LOGGER.debug("Received message: {}", message);
    }
}

When the scheduled job is invoked, the following line is written to the log:

2013-12-17 21:12:14,012 DEBUG - ScheduledJob            - Received message: Hello World!

Pretty cool. Right?

Summary

We have now successfully created scheduled jobs which can invoke secured method. This tutorial has taught us three things:

  • We learned that typically the SecurityContext object is stored to ThreadLocal which means that all scheduled jobs executed in the same thread share the same security context
  • We learned that if our application uses Spring Security 3.1, and we want to invoke a secured method from a scheduled job, the easiest way to do this is configure the used Authentication object in every scheduled job.
  • We learned how we can use the concurrency support of Spring Security 3.2 and transfer the SecurityContext object from one thread to another.

You can get the example applications of this blog post from Github (Spring Security 3.1 and Spring Security 3.2).

Note: The XML configuration of the Spring Security 3.2 example is not working at the moment. I will fix it when I have got time to do it.

31 comments… add one
  • Kai Virkki Apr 29, 2014 @ 17:03

    Thanks for a great post! This was more or less what I needed to implement and reading this post saved me time for sure.

    One note: What's the reason you set "ROLE_USER" as the credentials to the UserNamePasswordToken? Just for demonstration purposes?

    Cheers,
    -Kaitsu

    • Petri Apr 29, 2014 @ 18:14

      I set the credentials only because I didn't want to pass null to the constructor of the UsernamePasswordAuthenticationToken class. But now I started to wonder if setting the credentials is a bit confusing. What do you think?

      • Kai Virkki Apr 30, 2014 @ 16:21

        I think it's a bit confusing to put there some password when there's nobody who would really need it. But I guess we should have some other kind of implementation of Authentication than UsernamePasswordAuthenticationToken for this kind of situations, where we just want to set certain authorities. I didn't find anything suitable existing in Spring Security, so I ended up passing null, but didn't like that much better :/

        • Petri May 1, 2014 @ 18:05

          I spend some time thinking about this and figured out two alternative solutions to this problem:

          1. You could create a constant which name indicates that these values aren't required and use this constant when you create a new UsernamePasswordAuthenticationToken object.
          2. You could create a class which extends the UsernamePasswordAuthenticationToken class. The constructor of this class would have only one argument: a Collection<GrantedAuthority> object. This way you could hide the fact that you don't need to set the credentials in this case.

          I would probably use the first solution because it is simple and requires less code than the second solution.

      • David Jun 13, 2023 @ 16:08

        I tend to create a specific Authentication implementation just for this purpose to avoid confusion, e.g.:
        ```
        object InternalAuthenticationToken : AbstractAuthenticationToken() {
        override fun getCredentials() = null
        override fun getPrincipal() = null
        override fun isAuthenticated() = true
        }
        ```

        • Petri Jun 13, 2023 @ 22:35

          Hi David,

          That's a great idea. I will be using your idea the next time I have to do this.

  • Derek Sep 15, 2015 @ 13:38

    Thanks.
    Remember to put the clearSecurityContext in a finally block. Failure to do so could mean that the security context is leaked into the job's thread pool, which could be shared with other jobs.

    • Anonymous Sep 15, 2015 @ 13:38

      ...
      finally {
      AuthenticationUtil.clearAuthentication();
      }

      • Petri Sep 15, 2015 @ 17:48

        Hi Derek,

        Good point. I will add a warning about this to the blog post and give credits to you :) Thank you for pointing this out.

  • MJ Apr 13, 2016 @ 17:07

    Thanks for the helpful tutorial. Perhaps you'll know why I am running into the following issue:
    I'm using spring-security 4.02 with Spring Boot and I'm following the steps under "Spring Security 3.2: It Is Almost Like Magic!" The first time the task runs, the the SecurityContext is set, however during subsequent runs I am receiving the "An Authentication object was not found in the SecurityContext" error message. Any ideas?

    • MJ Apr 13, 2016 @ 17:16

      I should mention that the 3.1 instructions do work though.

    • Petri Apr 13, 2016 @ 19:51

      This is a bug. You can fix this by updating your Spring Security version. The bug report suggests that this is fixed in Spring Security 4.0.3.

  • Agus May 19, 2016 @ 22:43

    Hi,

    I followed your solution, especially the steps under “Spring Security 3.2: It Is Almost Like Magic!”. But seems it does not work. I alway received "An Authentication object was not found in the SecurityContext". Btw I use
    - Spring boot 1.2.7
    - Spring security 3.2.8.
    - SessionCreationPolicy.STATELESS in Session Management

    Try to use Spring Security 3.2.9, still does not work. do you have any idea?

    • Petri May 19, 2016 @ 22:54

      Hi,

      This is a known bug. The issue suggests that it should be fixed in Spring Security 3.2.9, but if this is not the case, you should create a new bug report (or update to Spring Security 4.0.3).

      • Agus May 19, 2016 @ 23:33

        Hi,

        Thanks for your fast response.
        I tried update to Spring Security 4.0.3, still does not work.
        Btw, I use spring boot runnable jar version using tomcat, and not using external web container.
        Do you think this affect the problem?

        I will recheck it again.
        Thanks

      • Agus May 20, 2016 @ 1:44

        Hi,

        Just to let you know, after mockup a sample project, it did work using Spring boot with Spring security 3.2.9, if I use @Scheduled annotation.
        The problem happen when I use xml configuration.
        In your sample code, you mentioned about xml configuration

      • Agus May 20, 2016 @ 2:33

        Finally I found a way to enable xml configuration without problem. Thanks

        
        @Bean(name="myTaskScheduler")
        public TaskScheduler getMyTaskScheduler() {
        	return new ConcurrentTaskScheduler((ScheduledExecutorService)taskExecutor());
        }
        
        <task:scheduled-tasks scheduler="myTaskScheduler">
        	<task:scheduled ref="myService" method="run" cron="0 0/1 * * * ?"/>
        </task:scheduled-tasks>
        
        
        • Petri May 21, 2016 @ 9:57

          You are welcome. I am happy to hear that you were able to solve your problem.

  • Jacky Jun 13, 2017 @ 12:55

    Hi @Petri!
    How to get information user login when using @Schedule SecurityContextHolder.getContext().getAuthentication().getPrincipal(). Plz help me!!

    • Petri Jun 13, 2017 @ 18:31

      Hi Jacky,

      If you run a scheduled job by using the @Scheduled annotation, you cannot (normally) get the user information because the action is invoked by the server and the security context is empty.

      However, if you have configured the logged in user by using the approach described in this blog post, you should be able to obtain the user information from the security context by using the SecurityContextHolder class.

      • Jacky Jun 14, 2017 @ 4:08

        Hi Petri.
        I'm using Spring security 4.0.4.RELEASE. How do I configure it?

        • Petri Jun 14, 2017 @ 12:29

          You can configure the Authentication object that is used when your scheduled job is run by using the concurrency support of Spring Security. Take a look at the section titled: 'Spring Security 3.2: It Is Almost Like Magic!'. This section explains how you can do it.

  • Ahmet Yaşar Özer Feb 27, 2018 @ 10:18

    Thanks a lot. Cheers.

    • Petri Mar 12, 2018 @ 8:44

      You are welcome.

  • Dinesh Jul 21, 2020 @ 11:09

    Thanks for this artilcle, with this article I was able fix my issue.

    • Petri Jul 23, 2020 @ 18:37

      You are welcome :)

  • Jacob Nov 20, 2020 @ 9:40

    Hi, thanks for writing this. How to do this in a microservice application? I followed this article and it did work in application A, but when I use feignclient to invoke another service method which is in application B, it returned error. The Authentication object in application B is null. What should I do to make the authentication object in B to be the same as the one in A? Thanks!

    • Petri Nov 25, 2020 @ 19:29

      Hi,

      If you invoke a service method of another micro service (via HTTP for example), you need to use the authentication method that's supported by the invoked micro service (like OAuth or basic auth). I am not familiar with FeignClient, but I assume that supports both of these authentication methods.

  • Wei Nov 23, 2022 @ 3:51

    Great article! You saved my career!

    • Petri Nov 29, 2022 @ 19:32

      I am happy to hear that I was able to help you out!

Leave a Reply