I have an AuthorizationComponent that allows me to check my user's roles. I use it in the methods of my various services.
Now, I try to create a job (using Jobrunr) that @Autowired one of these services, but it crashes because the SecurityContextHolder.getContext() is null.
So I'm trying to apply the answer I got here. Only, when I use DelegatingSecurityContextRunnable and my job is run, I get the following stacktrace:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.concurrent.DelegatingSecurityContextRunnable' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172) ~[spring-context-5.3.23.jar:5.3.23]
at org.jobrunr.server.runner.BackgroundJobWithIocRunner.supports(BackgroundJobWithIocRunner.java:21) ~[jobrunr-6.1.4.jar:6.1.4]
at org.jobrunr.server.BackgroundJobServer.lambda$getBackgroundJobRunner$0(BackgroundJobServer.java:224) ~[jobrunr-6.1.4.jar:6.1.4]
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:176) ~[?:?]
at java.util.Spliterators$ArraySpliterator.tryAdvance(Spliterators.java:958) ~[?:?]
at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127) ~[?:?]
at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502) ~[?:?]
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488) ~[?:?]
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[?:?]
at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150) ~[?:?]
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:?]
at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:543) ~[?:?]
at org.jobrunr.server.BackgroundJobServer.getBackgroundJobRunner(BackgroundJobServer.java:225) ~[jobrunr-6.1.4.jar:6.1.4]
at org.jobrunr.server.BackgroundJobPerformer.runActualJob(BackgroundJobPerformer.java:88) ~[jobrunr-6.1.4.jar:6.1.4]
at org.jobrunr.server.BackgroundJobPerformer.performJob(BackgroundJobPerformer.java:64) ~[jobrunr-6.1.4.jar:6.1.4]
at org.jobrunr.server.BackgroundJobPerformer.run(BackgroundJobPerformer.java:42) ~[jobrunr-6.1.4.jar:6.1.4]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[?:?]
at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
at java.lang.Thread.run(Thread.java:829)
Here's some code to give you some context:
Authorization component:
@Component
public class AuthorizationComponent {
public SecurityContext getSecurityContext() {
return SecurityContextHolder.getContext();
}
public Authentication getAuthentication() {
return this.getSecurityContext().getAuthentication();
}
public CustomUserDetails getUser() {
return (CustomUserDetails) this.getAuthentication().getPrincipal();
}
public boolean hasAnyRole(ERole...roles) {
return this.getAuthentication().getAuthorities().stream()
.anyMatch(grantedAuthority -> Arrays.stream(roles)
.map(Enum::toString)
.collect(Collectors.toList())
.contains(grantedAuthority.getAuthority())
);
}
public boolean hasSuperAdminRights() {
return this.hasAnyRole(ERole.ROLE_ADMIN);
}
public boolean hasAdminRights() {
return this.hasAnyRole(ERole.ROLE_ADMIN, ERole.ROLE_HR);
}
}
Jobrunr job :
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class InactiveUsersEmail implements JobRequest {
private Integer userId;
@Override
public Class<InactiveUsersEmailHandler> getJobRequestHandler() {
return InactiveUsersEmailHandler.class;
}
}
Define the recurring job:
@Service
@Transactional
@RequiredArgsConstructor
public class RecurringJobService {
private final AuthorizationComponent authorizationComponent;
@Recurring(id = "inactive-users-email", cron = "0 5 * * *")
@Job(name = "Sends email to inactive users")
public void sendInactiveUsersEmail() {
// for this example, only send user id 1
Runnable task = () -> new InactiveUsersEmail(1);
DelegatingSecurityContextRunnable securedTask = new DelegatingSecurityContextRunnable(task, this.authorizationComponent.getSecurityContext());
BackgroundJob.enqueue(securedTask::run);
}