Wrap a short-lived executor task with the submitting security context, then recheck authorization at the work boundary.
Spring SecurityContext handoff to a bounded async executor
A thread does not inherit request identity safely
An HTTP handler submits receipt work to a thread pool. SecurityContextHolder is normally associated with the executing thread; an ordinary worker does not automatically receive the caller's Authentication. DelegatingSecurityContextAsyncTaskExecutor wraps submissions so the task sees the captured context and clears it afterward. This is useful for short-lived request work. The transaction] is still separate, and trace context] has its own propagation path.
Keep authorization close to the effect
Capturing a security context does not make a queued task safe forever. The user may lose access before the task runs, or the token may expire. Pass a stable tenant and actor ID, re-evaluate any policy that must be current, and bound the queue so stale work does not pile up. For durable jobs, persist an explicit actor and command rather than a Java thread-local Authentication; durable delivery] has a different failure contract.
Test isolation between tasks
Submit two tasks from different authenticated callers and assert each sees only its own tenant. After each task, submit a task without authentication and assert it does not inherit the previous caller. Run rejection and cancellation cases through the bounded executor] to ensure no security context leaks when work never starts.
Implementation contract
@Bean("receiptSecurityExecutor")
AsyncTaskExecutor receiptSecurityExecutor(
@Qualifier("receiptExecutor") AsyncTaskExecutor pool) {
return new DelegatingSecurityContextAsyncTaskExecutor(pool);
}Cost and verification
Wrapping tasks adds context capture and restoration per submission. Queue delay is often the larger cost and can make a captured identity stale; keep capacity bounded and record the actor needed for audit.
Common Mistakes
- Do not assume request SecurityContext automatically appears on an arbitrary pool thread.
- Do not use a captured Authentication as the only authorization check for long-delayed work.
- Do not confuse security-context propagation with transaction or trace-context propagation.
Read next
Spring imperative transaction: a worker thread does not inherit it, Spring async trace context: follow work across executor threads, Spring async work versus durable delivery: separate latency from recovery, Spring task executors: reject work when every slot is occupied, Spring Data JPA auditing: record the actor at the write boundary.
