# spring-security-demo Companion repo for [Spring Security Context Propagation - Complete Guide](https://ankurm.com/spring-security-context-propagation-complete-guide/) on ankurm.com. Three small, dependency-light programs that answer one question each: does a Spring Security `SecurityContext` survive a specific thread hand-off? No web server, no HTTP -- every scenario sets an `Authentication` on the calling thread and checks whether the other side of the hand-off can see it. ## Verified versions | Component | Version | |---|---| | JDK | 25 (Temurin 25.0.4.1+1), LTS, GA 2025-09-16 | | Spring Boot (reference target) | 4.1.1 | | Spring Framework | 7.0.9 | | Spring Security | 7.1.1 | | `io.micrometer:context-propagation` | 1.2.1 (as managed by Boot 4.1.1's BOM) | `StructuredTaskScope` is a **preview API** on JDK 25 (JEP 505, fifth preview) and remains preview through JDK 26 (JEP 525, sixth preview) -- every build/run command below needs `--enable-preview`. ## Quickstart ```bash mvn dependency:build-classpath -Dmdep.outputFile=cp.txt javac --release 25 --enable-preview -cp "$(cat cp.txt)" -d target/classes $(find src -name '*.java') java --enable-preview -cp "target/classes:$(cat cp.txt)" com.ankurm.vt.Demo1PlainThreadLocal ``` Or just run everything and regenerate the captured output: `scripts/run-all.sh`. ## What each demo shows | Demo | Question | Chapter | |---|---|---| | `Demo1PlainThreadLocal` | Does `InheritableThreadLocal` behave differently for a pooled platform thread vs. a fresh virtual thread? (No Spring.) | [docs/01](docs/01-inheritable-threadlocal.md) | | `Demo2AsyncVirtualThreads` | Does the Boot-4.1-style virtual-thread `@Async` executor propagate `SecurityContext`, and what four fixes change? | [docs/02](docs/02-async-virtual-threads.md) | | `Demo3StructuredConcurrency` | Does a `StructuredTaskScope.fork()` subtask see the parent's `SecurityContext`? | [docs/03](docs/03-structured-concurrency.md) | ## Captured output Every number and log line in the blog post traces back to one of these, produced by `scripts/run-all.sh`, not retyped: - [docs/output/demo1.txt](docs/output/demo1.txt) - [docs/output/demo2.txt](docs/output/demo2.txt) - [docs/output/demo3.txt](docs/output/demo3.txt) ## The one-line summary of all three chapters `SecurityContextHolder` is a `ThreadLocal`. Nothing about virtual threads or structured concurrency changes that. What changed is that virtual threads are never pooled, so `MODE_INHERITABLETHREADLOCAL`'s old danger (stale context on a reused pool worker) doesn't apply to them -- and Spring Security 6.5 gave `SecurityContextHolder` a Micrometer `ThreadLocalAccessor`, so `ContextPropagatingTaskDecorator` / `ContextSnapshot.wrap(...)` now propagate it automatically, alongside MDC and tracing context, without a `DelegatingSecurityContext*` wrapper.