diff --git a/core/spring-security-core.gradle b/core/spring-security-core.gradle
index 7a326ed5918..3eb2343e3e3 100644
--- a/core/spring-security-core.gradle
+++ b/core/spring-security-core.gradle
@@ -16,6 +16,7 @@ dependencies {
api 'io.micrometer:micrometer-observation'
optional 'com.fasterxml.jackson.core:jackson-databind'
+ optional 'io.micrometer:context-propagation'
optional 'io.projectreactor:reactor-core'
optional 'jakarta.annotation:jakarta.annotation-api'
optional 'org.aspectj:aspectjrt'
diff --git a/core/src/main/java/org/springframework/security/core/context/ReactiveSecurityContextHolderThreadLocalAccessor.java b/core/src/main/java/org/springframework/security/core/context/ReactiveSecurityContextHolderThreadLocalAccessor.java
new file mode 100644
index 00000000000..6b7953f95b6
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/core/context/ReactiveSecurityContextHolderThreadLocalAccessor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2002-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.core.context;
+
+import io.micrometer.context.ThreadLocalAccessor;
+import reactor.core.publisher.Mono;
+
+import org.springframework.util.Assert;
+
+/**
+ * A {@link ThreadLocalAccessor} for accessing a {@link SecurityContext} with the
+ * {@link ReactiveSecurityContextHolder}.
+ *
+ * This class adapts the {@link ReactiveSecurityContextHolder} to the
+ * {@link ThreadLocalAccessor} contract to allow Micrometer Context Propagation to
+ * automatically propagate a {@link SecurityContext} in Reactive applications. It is
+ * automatically registered with the {@link io.micrometer.context.ContextRegistry} through
+ * the {@link java.util.ServiceLoader} mechanism when context-propagation is on the
+ * classpath.
+ *
+ * @author Steve Riesenberg
+ * @since 6.5
+ * @see io.micrometer.context.ContextRegistry
+ */
+public final class ReactiveSecurityContextHolderThreadLocalAccessor
+ implements ThreadLocalAccessor> {
+
+ private static final ThreadLocal> threadLocal = new ThreadLocal<>();
+
+ @Override
+ public Object key() {
+ return SecurityContext.class;
+ }
+
+ @Override
+ public Mono getValue() {
+ return threadLocal.get();
+ }
+
+ @Override
+ public void setValue(Mono securityContext) {
+ Assert.notNull(securityContext, "securityContext cannot be null");
+ threadLocal.set(securityContext);
+ }
+
+ @Override
+ public void setValue() {
+ threadLocal.remove();
+ }
+
+}
diff --git a/core/src/main/java/org/springframework/security/core/context/SecurityContextHolderThreadLocalAccessor.java b/core/src/main/java/org/springframework/security/core/context/SecurityContextHolderThreadLocalAccessor.java
new file mode 100644
index 00000000000..dbe17518666
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/core/context/SecurityContextHolderThreadLocalAccessor.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2002-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.core.context;
+
+import io.micrometer.context.ThreadLocalAccessor;
+
+/**
+ * A {@link ThreadLocalAccessor} for accessing a {@link SecurityContext} with the
+ * {@link SecurityContextHolder}.
+ *
+ * This class adapts the {@link SecurityContextHolder} to the {@link ThreadLocalAccessor}
+ * contract to allow Micrometer Context Propagation to automatically propagate a
+ * {@link SecurityContext} in Servlet applications. It is automatically registered with
+ * the {@link io.micrometer.context.ContextRegistry} through the
+ * {@link java.util.ServiceLoader} mechanism when context-propagation is on the classpath.
+ *
+ * @author Steve Riesenberg
+ * @since 6.5
+ * @see io.micrometer.context.ContextRegistry
+ */
+public final class SecurityContextHolderThreadLocalAccessor implements ThreadLocalAccessor {
+
+ @Override
+ public Object key() {
+ return SecurityContext.class.getName();
+ }
+
+ @Override
+ public SecurityContext getValue() {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ SecurityContext emptyContext = SecurityContextHolder.createEmptyContext();
+
+ return !securityContext.equals(emptyContext) ? securityContext : null;
+ }
+
+ @Override
+ public void setValue(SecurityContext value) {
+ SecurityContextHolder.setContext(value);
+ }
+
+ @Override
+ public void setValue() {
+ SecurityContextHolder.clearContext();
+ }
+
+}
diff --git a/core/src/main/resources/META-INF/spring/io.micrometer.context.ThreadLocalAccessor b/core/src/main/resources/META-INF/spring/io.micrometer.context.ThreadLocalAccessor
new file mode 100644
index 00000000000..65b406e6ae0
--- /dev/null
+++ b/core/src/main/resources/META-INF/spring/io.micrometer.context.ThreadLocalAccessor
@@ -0,0 +1,2 @@
+org.springframework.security.core.context.ReactiveSecurityContextHolderThreadLocalAccessor
+org.springframework.security.core.context.SecurityContextHolderThreadLocalAccessor
diff --git a/dependencies/spring-security-dependencies.gradle b/dependencies/spring-security-dependencies.gradle
index 7d69e3ec139..2a2d4332529 100644
--- a/dependencies/spring-security-dependencies.gradle
+++ b/dependencies/spring-security-dependencies.gradle
@@ -35,6 +35,7 @@ dependencies {
api libs.com.unboundid.unboundid.ldapsdk
api libs.commons.collections
api libs.io.mockk
+ api libs.io.micrometer.context.propagation
api libs.io.micrometer.micrometer.observation
api libs.jakarta.annotation.jakarta.annotation.api
api libs.jakarta.inject.jakarta.inject.api
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 92b427d1faa..a14c5c7a7e2 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -28,6 +28,7 @@ com-squareup-okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp", version.
com-unboundid-unboundid-ldapsdk = "com.unboundid:unboundid-ldapsdk:6.0.11"
com-unboundid-unboundid-ldapsdk7 = "com.unboundid:unboundid-ldapsdk:7.0.1"
commons-collections = "commons-collections:commons-collections:3.2.2"
+io-micrometer-context-propagation = "io.micrometer:context-propagation:1.1.2"
io-micrometer-micrometer-observation = "io.micrometer:micrometer-observation:1.14.4"
io-mockk = "io.mockk:mockk:1.13.16"
io-projectreactor-reactor-bom = "io.projectreactor:reactor-bom:2023.0.15"
diff --git a/web/spring-security-web.gradle b/web/spring-security-web.gradle
index a709c95a991..1a87fac63e9 100644
--- a/web/spring-security-web.gradle
+++ b/web/spring-security-web.gradle
@@ -36,6 +36,7 @@ dependencies {
api 'org.springframework:spring-web'
optional 'com.fasterxml.jackson.core:jackson-databind'
+ optional 'io.micrometer:context-propagation'
optional 'io.projectreactor:reactor-core'
optional 'org.springframework:spring-jdbc'
optional 'org.springframework:spring-tx'
diff --git a/web/src/main/java/org/springframework/security/web/server/ServerWebExchangeThreadLocalAccessor.java b/web/src/main/java/org/springframework/security/web/server/ServerWebExchangeThreadLocalAccessor.java
new file mode 100644
index 00000000000..efc45a201ad
--- /dev/null
+++ b/web/src/main/java/org/springframework/security/web/server/ServerWebExchangeThreadLocalAccessor.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2002-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.server;
+
+import io.micrometer.context.ThreadLocalAccessor;
+
+import org.springframework.util.Assert;
+import org.springframework.web.server.ServerWebExchange;
+
+/**
+ * A {@link ThreadLocalAccessor} for accessing a {@link ServerWebExchange}.
+ *
+ * This class adapts the existing Reactor Context attribute
+ * {@code ServerWebExchange.class} to the {@link ThreadLocalAccessor} contract to allow
+ * Micrometer Context Propagation to automatically propagate a {@link ServerWebExchange}
+ * in Reactive applications. It is automatically registered with the
+ * {@link io.micrometer.context.ContextRegistry} through the
+ * {@link java.util.ServiceLoader} mechanism when context-propagation is on the classpath.
+ *
+ * @author Steve Riesenberg
+ * @since 6.5
+ * @see io.micrometer.context.ContextRegistry
+ */
+public final class ServerWebExchangeThreadLocalAccessor implements ThreadLocalAccessor {
+
+ private static final ThreadLocal threadLocal = new ThreadLocal<>();
+
+ @Override
+ public Object key() {
+ return ServerWebExchange.class;
+ }
+
+ @Override
+ public ServerWebExchange getValue() {
+ return threadLocal.get();
+ }
+
+ @Override
+ public void setValue(ServerWebExchange exchange) {
+ Assert.notNull(exchange, "exchange must not be null");
+ threadLocal.set(exchange);
+ }
+
+ @Override
+ public void setValue() {
+ threadLocal.remove();
+ }
+
+}
diff --git a/web/src/main/resources/META-INF/spring/io.micrometer.context.ThreadLocalAccessor b/web/src/main/resources/META-INF/spring/io.micrometer.context.ThreadLocalAccessor
new file mode 100644
index 00000000000..63959839f8b
--- /dev/null
+++ b/web/src/main/resources/META-INF/spring/io.micrometer.context.ThreadLocalAccessor
@@ -0,0 +1 @@
+org.springframework.security.web.server.ServerWebExchangeThreadLocalAccessor