Skip to content

Commit

Permalink
Add support for automatic context-propagation with Micrometer
Browse files Browse the repository at this point in the history
  • Loading branch information
sjohnr committed Feb 26, 2025
1 parent f2d78a0 commit 1a16799
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/spring-security-core.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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}.
* <p>
* 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<Mono<SecurityContext>> {

private static final ThreadLocal<Mono<SecurityContext>> threadLocal = new ThreadLocal<>();

@Override
public Object key() {
return SecurityContext.class;
}

@Override
public Mono<SecurityContext> getValue() {
return threadLocal.get();
}

@Override
public void setValue(Mono<SecurityContext> securityContext) {
Assert.notNull(securityContext, "securityContext cannot be null");
threadLocal.set(securityContext);
}

@Override
public void setValue() {
threadLocal.remove();
}

}
Original file line number Diff line number Diff line change
@@ -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}.
* <p>
* 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<SecurityContext> {

@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();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.security.core.context.ReactiveSecurityContextHolderThreadLocalAccessor
org.springframework.security.core.context.SecurityContextHolderThreadLocalAccessor
1 change: 1 addition & 0 deletions dependencies/spring-security-dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions web/spring-security-web.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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}.
* <p>
* 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<ServerWebExchange> {

private static final ThreadLocal<ServerWebExchange> 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();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.springframework.security.web.server.ServerWebExchangeThreadLocalAccessor

0 comments on commit 1a16799

Please sign in to comment.