Abstract ObservationRegistry Behind ObjectPostProcessor

Issue gh-15678
This commit is contained in:
Josh Cummings 2024-09-19 09:43:38 -06:00
parent 1ed20aa210
commit 69e3c248fa
No known key found for this signature in database
GPG Key ID: A306A51F43B8E5A5
27 changed files with 645 additions and 146 deletions

View File

@ -31,6 +31,15 @@ import org.springframework.beans.factory.InitializingBean;
*/ */
public interface ObjectPostProcessor<T> { public interface ObjectPostProcessor<T> {
static <S> ObjectPostProcessor<S> identity() {
return new ObjectPostProcessor<>() {
@Override
public <O extends S> O postProcess(O object) {
return object;
}
};
}
/** /**
* Initialize the object possibly returning a modified instance that should be used * Initialize the object possibly returning a modified instance that should be used
* instead. * instead.

View File

@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.method.configuration;
import java.util.function.Supplier; import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; import org.aopalliance.intercept.MethodInvocation;
@ -36,9 +35,9 @@ import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthoritiesAuthorizationManager; import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.ObservationAuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor; import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.Jsr250AuthorizationManager; import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.context.SecurityContextHolderStrategy;
@ -58,8 +57,15 @@ final class Jsr250MethodSecurityConfiguration implements ImportAware, AopInfrast
private final Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager(); private final Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager();
private AuthorizationManagerBeforeMethodInterceptor methodInterceptor = AuthorizationManagerBeforeMethodInterceptor private final AuthorizationManagerBeforeMethodInterceptor methodInterceptor;
.jsr250(this.authorizationManager);
Jsr250MethodSecurityConfiguration(
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> postProcessors) {
ObjectPostProcessor<AuthorizationManager<MethodInvocation>> postProcessor = postProcessors
.getIfUnique(ObjectPostProcessor::identity);
AuthorizationManager<MethodInvocation> manager = postProcessor.postProcess(this.authorizationManager);
this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.jsr250(manager);
}
@Bean @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ -95,16 +101,6 @@ final class Jsr250MethodSecurityConfiguration implements ImportAware, AopInfrast
this.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy); this.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
} }
@Autowired(required = false)
void setObservationRegistry(ObservationRegistry registry) {
if (registry.isNoop()) {
return;
}
AuthorizationManager<MethodInvocation> observed = new ObservationAuthorizationManager<>(registry,
this.authorizationManager);
this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.secured(observed);
}
@Autowired(required = false) @Autowired(required = false)
void setEventPublisher(AuthorizationEventPublisher eventPublisher) { void setEventPublisher(AuthorizationEventPublisher eventPublisher) {
this.methodInterceptor.setAuthorizationEventPublisher(eventPublisher); this.methodInterceptor.setAuthorizationEventPublisher(eventPublisher);

View File

@ -41,6 +41,9 @@ final class MethodSecuritySelector implements ImportSelector {
private static final boolean isDataPresent = ClassUtils private static final boolean isDataPresent = ClassUtils
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null); .isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
private static final boolean isObservabilityPresent = ClassUtils
.isPresent("io.micrometer.observation.ObservationRegistry", null);
private final ImportSelector autoProxy = new AutoProxyRegistrarSelector(); private final ImportSelector autoProxy = new AutoProxyRegistrarSelector();
@Override @Override
@ -64,6 +67,10 @@ final class MethodSecuritySelector implements ImportSelector {
if (isDataPresent) { if (isDataPresent) {
imports.add(AuthorizationProxyDataConfiguration.class.getName()); imports.add(AuthorizationProxyDataConfiguration.class.getName());
} }
if (isObservabilityPresent) {
imports.add(
"org.springframework.security.config.annotation.observation.configuration.ObservationConfiguration");
}
return imports.toArray(new String[0]); return imports.toArray(new String[0]);
} }

View File

@ -16,8 +16,8 @@
package org.springframework.security.config.annotation.method.configuration; package org.springframework.security.config.annotation.method.configuration;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut; import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.aop.framework.AopInfrastructureBean;
@ -38,14 +38,16 @@ import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.aot.hint.PrePostAuthorizeHintsRegistrar; import org.springframework.security.aot.hint.PrePostAuthorizeHintsRegistrar;
import org.springframework.security.aot.hint.SecurityHintsRegistrar; import org.springframework.security.aot.hint.SecurityHintsRegistrar;
import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.ObservationAuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor; import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor; import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager; import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor; import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager; import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor; import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.PrePostTemplateDefaults; import org.springframework.security.authorization.method.PrePostTemplateDefaults;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults; import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.context.SecurityContextHolderStrategy;
@ -78,21 +80,29 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, Applicati
private final PreFilterAuthorizationMethodInterceptor preFilterMethodInterceptor = new PreFilterAuthorizationMethodInterceptor(); private final PreFilterAuthorizationMethodInterceptor preFilterMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
private AuthorizationManagerBeforeMethodInterceptor preAuthorizeMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor private final AuthorizationManagerBeforeMethodInterceptor preAuthorizeMethodInterceptor;
.preAuthorize(this.preAuthorizeAuthorizationManager);
private AuthorizationManagerAfterMethodInterceptor postAuthorizeMethodInterceptor = AuthorizationManagerAfterMethodInterceptor private final AuthorizationManagerAfterMethodInterceptor postAuthorizeMethodInterceptor;
.postAuthorize(this.postAuthorizeAuthorizationManager);
private final PostFilterAuthorizationMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationMethodInterceptor(); private final PostFilterAuthorizationMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationMethodInterceptor();
private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
{ PrePostMethodSecurityConfiguration(
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> preAuthorizeProcessor,
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocationResult>>> postAuthorizeProcessor) {
this.preFilterMethodInterceptor.setExpressionHandler(this.expressionHandler); this.preFilterMethodInterceptor.setExpressionHandler(this.expressionHandler);
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler); this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
this.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler); this.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
this.postFilterMethodInterceptor.setExpressionHandler(this.expressionHandler); this.postFilterMethodInterceptor.setExpressionHandler(this.expressionHandler);
AuthorizationManager<MethodInvocation> preAuthorize = preAuthorizeProcessor
.getIfUnique(ObjectPostProcessor::identity)
.postProcess(this.preAuthorizeAuthorizationManager);
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor.preAuthorize(preAuthorize);
AuthorizationManager<MethodInvocationResult> postAuthorize = postAuthorizeProcessor
.getIfUnique(ObjectPostProcessor::identity)
.postProcess(this.postAuthorizeAuthorizationManager);
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterMethodInterceptor.postAuthorize(postAuthorize);
} }
@Override @Override
@ -144,17 +154,6 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, Applicati
this.postFilterMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy); this.postFilterMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
} }
@Autowired(required = false)
void setObservationRegistry(ObservationRegistry registry) {
if (registry.isNoop()) {
return;
}
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor
.preAuthorize(new ObservationAuthorizationManager<>(registry, this.preAuthorizeAuthorizationManager));
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterMethodInterceptor
.postAuthorize(new ObservationAuthorizationManager<>(registry, this.postAuthorizeAuthorizationManager));
}
@Autowired(required = false) @Autowired(required = false)
void setAuthorizationEventPublisher(AuthorizationEventPublisher publisher) { void setAuthorizationEventPublisher(AuthorizationEventPublisher publisher) {
this.preAuthorizeMethodInterceptor.setAuthorizationEventPublisher(publisher); this.preAuthorizeMethodInterceptor.setAuthorizationEventPublisher(publisher);

View File

@ -16,8 +16,8 @@
package org.springframework.security.config.annotation.method.configuration; package org.springframework.security.config.annotation.method.configuration;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut; import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.aop.framework.AopInfrastructureBean;
@ -34,14 +34,16 @@ import org.springframework.context.annotation.Role;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager; import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor; import org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor; import org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.PostAuthorizeReactiveAuthorizationManager; import org.springframework.security.authorization.method.PostAuthorizeReactiveAuthorizationManager;
import org.springframework.security.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor; import org.springframework.security.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor;
import org.springframework.security.authorization.method.PreAuthorizeReactiveAuthorizationManager; import org.springframework.security.authorization.method.PreAuthorizeReactiveAuthorizationManager;
import org.springframework.security.authorization.method.PreFilterAuthorizationReactiveMethodInterceptor; import org.springframework.security.authorization.method.PreFilterAuthorizationReactiveMethodInterceptor;
import org.springframework.security.authorization.method.PrePostTemplateDefaults; import org.springframework.security.authorization.method.PrePostTemplateDefaults;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults; import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
@ -77,22 +79,30 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration
private PostFilterAuthorizationReactiveMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor(); private PostFilterAuthorizationReactiveMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor();
private AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeMethodInterceptor; private final AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeMethodInterceptor;
private AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor; private final AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;
@Autowired(required = false) @Autowired(required = false)
ReactiveAuthorizationManagerMethodSecurityConfiguration(MethodSecurityExpressionHandler expressionHandler) { ReactiveAuthorizationManagerMethodSecurityConfiguration(MethodSecurityExpressionHandler expressionHandler,
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>>> preAuthorizePostProcessor,
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>>> postAuthorizePostProcessor) {
if (expressionHandler != null) { if (expressionHandler != null) {
this.preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler); this.preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
this.preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager(expressionHandler); this.preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager(expressionHandler);
this.postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor(expressionHandler); this.postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
this.postAuthorizeAuthorizationManager = new PostAuthorizeReactiveAuthorizationManager(expressionHandler); this.postAuthorizeAuthorizationManager = new PostAuthorizeReactiveAuthorizationManager(expressionHandler);
} }
ReactiveAuthorizationManager<MethodInvocation> preAuthorize = preAuthorizePostProcessor
.getIfUnique(ObjectPostProcessor::identity)
.postProcess(this.preAuthorizeAuthorizationManager);
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeReactiveMethodInterceptor this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeReactiveMethodInterceptor
.preAuthorize(this.preAuthorizeAuthorizationManager); .preAuthorize(preAuthorize);
ReactiveAuthorizationManager<MethodInvocationResult> postAuthorize = postAuthorizePostProcessor
.getIfAvailable(ObjectPostProcessor::identity)
.postProcess(this.postAuthorizeAuthorizationManager);
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterReactiveMethodInterceptor this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterReactiveMethodInterceptor
.postAuthorize(this.postAuthorizeAuthorizationManager); .postAuthorize(postAuthorize);
} }
@Override @Override
@ -117,17 +127,6 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration
this.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults); this.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
} }
@Autowired(required = false)
void setObservationRegistry(ObservationRegistry registry) {
if (registry.isNoop()) {
return;
}
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(
new ObservationReactiveAuthorizationManager<>(registry, this.preAuthorizeAuthorizationManager));
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(
new ObservationReactiveAuthorizationManager<>(registry, this.postAuthorizeAuthorizationManager));
}
@Bean @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static MethodInterceptor preFilterAuthorizationMethodInterceptor( static MethodInterceptor preFilterAuthorizationMethodInterceptor(

View File

@ -38,6 +38,9 @@ class ReactiveMethodSecuritySelector implements ImportSelector {
private static final boolean isDataPresent = ClassUtils private static final boolean isDataPresent = ClassUtils
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null); .isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
private static final boolean isObservabilityPresent = ClassUtils
.isPresent("io.micrometer.observation.ObservationRegistry", null);
private final ImportSelector autoProxy = new AutoProxyRegistrarSelector(); private final ImportSelector autoProxy = new AutoProxyRegistrarSelector();
@Override @Override
@ -58,6 +61,10 @@ class ReactiveMethodSecuritySelector implements ImportSelector {
if (isDataPresent) { if (isDataPresent) {
imports.add(AuthorizationProxyDataConfiguration.class.getName()); imports.add(AuthorizationProxyDataConfiguration.class.getName());
} }
if (isObservabilityPresent) {
imports.add(
"org.springframework.security.config.annotation.observation.configuration.ReactiveObservationConfiguration");
}
imports.add(AuthorizationProxyConfiguration.class.getName()); imports.add(AuthorizationProxyConfiguration.class.getName());
return imports.toArray(new String[0]); return imports.toArray(new String[0]);
} }

View File

@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.method.configuration;
import java.util.function.Supplier; import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; import org.aopalliance.intercept.MethodInvocation;
@ -37,9 +36,9 @@ import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthoritiesAuthorizationManager; import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.ObservationAuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor; import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.SecuredAuthorizationManager; import org.springframework.security.authorization.method.SecuredAuthorizationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.context.SecurityContextHolderStrategy;
/** /**
@ -58,8 +57,15 @@ final class SecuredMethodSecurityConfiguration implements ImportAware, AopInfras
private final SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager(); private final SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager();
private AuthorizationManagerBeforeMethodInterceptor methodInterceptor = AuthorizationManagerBeforeMethodInterceptor private final AuthorizationManagerBeforeMethodInterceptor methodInterceptor;
.secured(this.authorizationManager);
SecuredMethodSecurityConfiguration(
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> postProcessors) {
ObjectPostProcessor<AuthorizationManager<MethodInvocation>> postProcessor = postProcessors
.getIfUnique(ObjectPostProcessor::identity);
AuthorizationManager<MethodInvocation> manager = postProcessor.postProcess(this.authorizationManager);
this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.secured(manager);
}
@Bean @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ -90,16 +96,6 @@ final class SecuredMethodSecurityConfiguration implements ImportAware, AopInfras
this.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy); this.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
} }
@Autowired(required = false)
void setObservationRegistry(ObservationRegistry registry) {
if (registry.isNoop()) {
return;
}
AuthorizationManager<MethodInvocation> observed = new ObservationAuthorizationManager<>(registry,
this.authorizationManager);
this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.secured(observed);
}
@Autowired(required = false) @Autowired(required = false)
void setEventPublisher(AuthorizationEventPublisher eventPublisher) { void setEventPublisher(AuthorizationEventPublisher eventPublisher) {
this.methodInterceptor.setAuthorizationEventPublisher(eventPublisher); this.methodInterceptor.setAuthorizationEventPublisher(eventPublisher);

View File

@ -0,0 +1,53 @@
/*
* Copyright 2002-2024 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.config.annotation.observation.configuration;
import java.util.function.BiFunction;
import java.util.function.Function;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
abstract class AbstractObservationObjectPostProcessor<O> implements ObjectPostProcessor<O> {
private final ObjectProvider<ObservationRegistry> observationRegistry;
private final BiFunction<ObservationRegistry, O, O> wrapper;
AbstractObservationObjectPostProcessor(ObjectProvider<ObservationRegistry> observationRegistry,
Function<ObservationRegistry, O> constructor) {
this(observationRegistry, (registry, object) -> constructor.apply(registry));
}
AbstractObservationObjectPostProcessor(ObjectProvider<ObservationRegistry> observationRegistry,
BiFunction<ObservationRegistry, O, O> constructor) {
this.observationRegistry = observationRegistry;
this.wrapper = constructor;
}
@Override
public <O1 extends O> O1 postProcess(O1 object) {
ObservationRegistry registry = this.observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP);
if (registry.isNoop()) {
return object;
}
return (O1) this.wrapper.apply(registry, object);
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2002-2024 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.config.annotation.observation.configuration;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.http.HttpServletRequest;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ObservationAuthenticationManager;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.ObservationAuthorizationManager;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.ObservationFilterChainDecorator;
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
class ObservationConfiguration {
private final ObjectProvider<ObservationRegistry> observationRegistry;
ObservationConfiguration(ObjectProvider<ObservationRegistry> observationRegistry) {
this.observationRegistry = observationRegistry;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<AuthorizationManager<MethodInvocation>> methodAuthorizationManagerPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationAuthorizationManager::new) {
};
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<AuthorizationManager<MethodInvocationResult>> methodResultAuthorizationManagerPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationAuthorizationManager::new) {
};
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<AuthorizationManager<HttpServletRequest>> webAuthorizationManagerPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationAuthorizationManager::new) {
};
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<AuthenticationManager> authenticationManagerPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationAuthenticationManager::new) {
};
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<FilterChainProxy.FilterChainDecorator> filterChainDecoratorPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationFilterChainDecorator::new) {
};
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2002-2024 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.config.annotation.observation.configuration;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.security.authentication.ObservationReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.web.server.ObservationWebFilterChainDecorator;
import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.web.server.ServerWebExchange;
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
class ReactiveObservationConfiguration {
private final ObjectProvider<ObservationRegistry> observationRegistry;
ReactiveObservationConfiguration(ObjectProvider<ObservationRegistry> observationRegistry) {
this.observationRegistry = observationRegistry;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>> methodAuthorizationManagerPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationReactiveAuthorizationManager::new) {
};
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>> methodResultAuthorizationManagerPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationReactiveAuthorizationManager::new) {
};
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<ReactiveAuthorizationManager<ServerWebExchange>> webAuthorizationManagerPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationReactiveAuthorizationManager::new) {
};
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<ReactiveAuthenticationManager> authenticationManagerPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationReactiveAuthenticationManager::new) {
};
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<WebFilterChainProxy.WebFilterChainDecorator> filterChainDecoratorPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationWebFilterChainDecorator::new) {
};
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2002-2024 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.config.annotation.observation.configuration;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.messaging.Message;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.ObservationAuthorizationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
class WebSocketObservationConfiguration {
private final ObjectProvider<ObservationRegistry> observationRegistry;
WebSocketObservationConfiguration(ObjectProvider<ObservationRegistry> observationRegistry) {
this.observationRegistry = observationRegistry;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
ObjectPostProcessor<AuthorizationManager<Message<?>>> messageAuthorizationManagerPostProcessor() {
return new AbstractObservationObjectPostProcessor<>(this.observationRegistry,
ObservationAuthorizationManager::new) {
};
}
}

View File

@ -35,7 +35,8 @@ import org.springframework.context.annotation.Import;
@Documented @Documented
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Import({ RSocketSecurityConfiguration.class, SecuritySocketAcceptorInterceptorConfiguration.class }) @Import({ RSocketSecurityConfiguration.class, SecuritySocketAcceptorInterceptorConfiguration.class,
ReactiveObservationImportSelector.class })
public @interface EnableRSocketSecurity { public @interface EnableRSocketSecurity {
} }

View File

@ -16,16 +16,14 @@
package org.springframework.security.config.annotation.rsocket; package org.springframework.security.config.annotation.rsocket;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.security.authentication.ObservationReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager; import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService; import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
@ -46,7 +44,7 @@ class RSocketSecurityConfiguration {
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP; private ObjectPostProcessor<ReactiveAuthenticationManager> postProcessor = ObjectPostProcessor.identity();
@Autowired(required = false) @Autowired(required = false)
void setAuthenticationManager(ReactiveAuthenticationManager authenticationManager) { void setAuthenticationManager(ReactiveAuthenticationManager authenticationManager) {
@ -64,8 +62,8 @@ class RSocketSecurityConfiguration {
} }
@Autowired(required = false) @Autowired(required = false)
void setObservationRegistry(ObservationRegistry observationRegistry) { void setAuthenticationManagerPostProcessor(ObjectPostProcessor<ReactiveAuthenticationManager> postProcessor) {
this.observationRegistry = observationRegistry; this.postProcessor = postProcessor;
} }
@Bean(name = RSOCKET_SECURITY_BEAN_NAME) @Bean(name = RSOCKET_SECURITY_BEAN_NAME)
@ -86,10 +84,7 @@ class RSocketSecurityConfiguration {
if (this.passwordEncoder != null) { if (this.passwordEncoder != null) {
manager.setPasswordEncoder(this.passwordEncoder); manager.setPasswordEncoder(this.passwordEncoder);
} }
if (!this.observationRegistry.isNoop()) { return this.postProcessor.postProcess(manager);
return new ObservationReactiveAuthenticationManager(this.observationRegistry, manager);
}
return manager;
} }
return null; return null;
} }

View File

@ -0,0 +1,51 @@
/*
* Copyright 2002-2013 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.config.annotation.rsocket;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.util.ClassUtils;
/**
* Used by {@link EnableWebFluxSecurity} to conditionally import observation configuration
* when {@link ObservationRegistry} is present.
*
* @author Josh Cummings
* @since 6.4
*/
class ReactiveObservationImportSelector implements ImportSelector {
private static final boolean observabilityPresent;
static {
ClassLoader classLoader = ReactiveObservationImportSelector.class.getClassLoader();
observabilityPresent = ClassUtils.isPresent("io.micrometer.observation.ObservationRegistry", classLoader);
}
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
if (!observabilityPresent) {
return new String[0];
}
return new String[] {
"org.springframework.security.config.annotation.observation.configuration.ReactiveObservationConfiguration" };
}
}

View File

@ -21,7 +21,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.Filter; import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
@ -30,12 +29,13 @@ import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.core.OrderComparator; import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ObservationAuthenticationManager;
import org.springframework.security.config.Customizer; import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder; import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.ObjectPostProcessor;
@ -3279,13 +3279,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
setSharedObject(AuthenticationManager.class, this.authenticationManager); setSharedObject(AuthenticationManager.class, this.authenticationManager);
} }
else { else {
ObservationRegistry registry = getObservationRegistry(); ObjectPostProcessor<AuthenticationManager> postProcessor = getAuthenticationManagerPostProcessor();
AuthenticationManager manager = getAuthenticationRegistry().build(); AuthenticationManager manager = getAuthenticationRegistry().build();
if (!registry.isNoop() && manager != null) { if (manager != null) {
setSharedObject(AuthenticationManager.class, new ObservationAuthenticationManager(registry, manager)); setSharedObject(AuthenticationManager.class, postProcessor.postProcess(manager));
}
else {
setSharedObject(AuthenticationManager.class, manager);
} }
} }
} }
@ -3723,8 +3720,12 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
return apply(configurer); return apply(configurer);
} }
private ObservationRegistry getObservationRegistry() { private ObjectPostProcessor<AuthenticationManager> getAuthenticationManagerPostProcessor() {
return getContext().getBeanProvider(ObservationRegistry.class).getIfUnique(() -> ObservationRegistry.NOOP); ApplicationContext context = getContext();
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,
AuthenticationManager.class);
ObjectProvider<ObjectPostProcessor<AuthenticationManager>> manager = context.getBeanProvider(type);
return manager.getIfUnique(ObjectPostProcessor::identity);
} }
/** /**

View File

@ -28,8 +28,10 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.core.ResolvableType;
import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.SecurityExpressionHandler; import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
@ -45,8 +47,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterChainProxy.FilterChainDecorator;
import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.ObservationFilterChainDecorator;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator; import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer; import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer;
@ -110,6 +112,9 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP; private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
private ObjectPostProcessor<FilterChainDecorator> filterChainDecoratorPostProcessor = ObjectPostProcessor
.identity();
private HttpServletRequestTransformer privilegeEvaluatorRequestTransformer; private HttpServletRequestTransformer privilegeEvaluatorRequestTransformer;
private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler(); private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
@ -403,6 +408,11 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
} }
catch (NoSuchBeanDefinitionException ex) { catch (NoSuchBeanDefinitionException ex) {
} }
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,
FilterChainDecorator.class);
ObjectProvider<ObjectPostProcessor<FilterChainDecorator>> postProcessor = applicationContext
.getBeanProvider(type);
this.filterChainDecoratorPostProcessor = postProcessor.getIfUnique(ObjectPostProcessor::identity);
Class<HttpServletRequestTransformer> requestTransformerClass = HttpServletRequestTransformer.class; Class<HttpServletRequestTransformer> requestTransformerClass = HttpServletRequestTransformer.class;
this.privilegeEvaluatorRequestTransformer = applicationContext.getBeanProvider(requestTransformerClass) this.privilegeEvaluatorRequestTransformer = applicationContext.getBeanProvider(requestTransformerClass)
.getIfUnique(); .getIfUnique();
@ -413,11 +423,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
this.servletContext = servletContext; this.servletContext = servletContext;
} }
FilterChainProxy.FilterChainDecorator getFilterChainDecorator() { FilterChainDecorator getFilterChainDecorator() {
if (this.observationRegistry.isNoop()) { return this.filterChainDecoratorPostProcessor.postProcess(new FilterChainProxy.VirtualFilterChainDecorator());
return new FilterChainProxy.VirtualFilterChainDecorator();
}
return new ObservationFilterChainDecorator(this.observationRegistry);
} }
/** /**

View File

@ -82,7 +82,7 @@ import org.springframework.security.web.SecurityFilterChain;
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Documented @Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
HttpSecurityConfiguration.class }) HttpSecurityConfiguration.class, ObservationImportSelector.class })
@EnableGlobalAuthentication @EnableGlobalAuthentication
public @interface EnableWebSecurity { public @interface EnableWebSecurity {

View File

@ -0,0 +1,50 @@
/*
* Copyright 2002-2013 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.config.annotation.web.configuration;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
/**
* Used by {@link EnableWebSecurity} to conditionally import observation configuration
* when {@link ObservationRegistry} is present.
*
* @author Josh Cummings
* @since 6.4
*/
class ObservationImportSelector implements ImportSelector {
private static final boolean observabilityPresent;
static {
ClassLoader classLoader = ObservationImportSelector.class.getClassLoader();
observabilityPresent = ClassUtils.isPresent("io.micrometer.observation.ObservationRegistry", classLoader);
}
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
if (!observabilityPresent) {
return new String[0];
}
return new String[] {
"org.springframework.security.config.annotation.observation.configuration.ObservationConfiguration" };
}
}

View File

@ -20,10 +20,11 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy; import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthenticatedAuthorizationManager; import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
@ -32,7 +33,6 @@ import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationManagers; import org.springframework.security.authorization.AuthorizationManagers;
import org.springframework.security.authorization.ObservationAuthorizationManager;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher; import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry; import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
@ -68,6 +68,9 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
private String rolePrefix = "ROLE_"; private String rolePrefix = "ROLE_";
private ObjectPostProcessor<AuthorizationManager<HttpServletRequest>> postProcessor = ObjectPostProcessor
.identity();
/** /**
* Creates an instance. * Creates an instance.
* @param context the {@link ApplicationContext} to use * @param context the {@link ApplicationContext} to use
@ -87,6 +90,11 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(GrantedAuthorityDefaults.class); GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(GrantedAuthorityDefaults.class);
this.rolePrefix = grantedAuthorityDefaults.getRolePrefix(); this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
} }
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,
ResolvableType.forClassWithGenerics(AuthorizationManager.class, HttpServletRequest.class));
ObjectProvider<ObjectPostProcessor<AuthorizationManager<HttpServletRequest>>> provider = context
.getBeanProvider(type);
provider.ifUnique((postProcessor) -> this.postProcessor = postProcessor);
} }
/** /**
@ -123,17 +131,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
return this.registry; return this.registry;
} }
private ObservationRegistry getObservationRegistry() {
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
String[] names = context.getBeanNamesForType(ObservationRegistry.class);
if (names.length == 1) {
return context.getBean(ObservationRegistry.class);
}
else {
return ObservationRegistry.NOOP;
}
}
/** /**
* Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}. * Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
* *
@ -173,12 +170,8 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
+ ". Try completing it with something like requestUrls().<something>.hasRole('USER')"); + ". Try completing it with something like requestUrls().<something>.hasRole('USER')");
Assert.state(this.mappingCount > 0, Assert.state(this.mappingCount > 0,
"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())"); "At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())");
ObservationRegistry registry = getObservationRegistry();
RequestMatcherDelegatingAuthorizationManager manager = postProcess(this.managerBuilder.build()); RequestMatcherDelegatingAuthorizationManager manager = postProcess(this.managerBuilder.build());
if (registry.isNoop()) { return AuthorizeHttpRequestsConfigurer.this.postProcessor.postProcess(manager);
return manager;
}
return new ObservationAuthorizationManager<>(registry, manager);
} }
@Override @Override

View File

@ -86,7 +86,7 @@ import org.springframework.security.config.web.server.ServerHttpSecurity;
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Documented @Documented
@Import({ ServerHttpSecurityConfiguration.class, WebFluxSecurityConfiguration.class, @Import({ ServerHttpSecurityConfiguration.class, WebFluxSecurityConfiguration.class,
ReactiveOAuth2ClientImportSelector.class }) ReactiveOAuth2ClientImportSelector.class, ReactiveObservationImportSelector.class })
public @interface EnableWebFluxSecurity { public @interface EnableWebFluxSecurity {
} }

View File

@ -0,0 +1,50 @@
/*
* Copyright 2002-2013 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.config.annotation.web.reactive;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
/**
* Used by {@link EnableWebFluxSecurity} to conditionally import observation configuration
* when {@link ObservationRegistry} is present.
*
* @author Josh Cummings
* @since 6.4
*/
class ReactiveObservationImportSelector implements ImportSelector {
private static final boolean observabilityPresent;
static {
ClassLoader classLoader = ReactiveObservationImportSelector.class.getClassLoader();
observabilityPresent = ClassUtils.isPresent("io.micrometer.observation.ObservationRegistry", classLoader);
}
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
if (!observabilityPresent) {
return new String[0];
}
return new String[] {
"org.springframework.security.config.annotation.observation.configuration.ReactiveObservationConfiguration" };
}
}

View File

@ -16,8 +16,6 @@
package org.springframework.security.config.annotation.web.reactive; package org.springframework.security.config.annotation.web.reactive;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
@ -29,10 +27,10 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.security.authentication.ObservationReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager; import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker; import org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults; import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService; import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService;
@ -67,7 +65,7 @@ class ServerHttpSecurityConfiguration {
private ReactiveCompromisedPasswordChecker compromisedPasswordChecker; private ReactiveCompromisedPasswordChecker compromisedPasswordChecker;
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP; private ObjectPostProcessor<ReactiveAuthenticationManager> postProcessor = ObjectPostProcessor.identity();
@Autowired(required = false) @Autowired(required = false)
private BeanFactory beanFactory; private BeanFactory beanFactory;
@ -98,8 +96,8 @@ class ServerHttpSecurityConfiguration {
} }
@Autowired(required = false) @Autowired(required = false)
void setObservationRegistry(ObservationRegistry observationRegistry) { void setAuthenticationManagerPostProcessor(ObjectPostProcessor<ReactiveAuthenticationManager> postProcessor) {
this.observationRegistry = observationRegistry; this.postProcessor = postProcessor;
} }
@Autowired(required = false) @Autowired(required = false)
@ -169,10 +167,7 @@ class ServerHttpSecurityConfiguration {
} }
manager.setUserDetailsPasswordService(this.userDetailsPasswordService); manager.setUserDetailsPasswordService(this.userDetailsPasswordService);
manager.setCompromisedPasswordChecker(this.compromisedPasswordChecker); manager.setCompromisedPasswordChecker(this.compromisedPasswordChecker);
if (!this.observationRegistry.isNoop()) { return this.postProcessor.postProcess(manager);
return new ObservationReactiveAuthenticationManager(this.observationRegistry, manager);
}
return manager;
} }
return null; return null;
} }

View File

@ -19,20 +19,20 @@ package org.springframework.security.config.annotation.web.reactive;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor; import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;
import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor; import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;
import org.springframework.security.web.server.ObservationWebFilterChainDecorator;
import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterChainProxy; import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.security.web.server.WebFilterChainProxy.DefaultWebFilterChainDecorator;
import org.springframework.security.web.server.WebFilterChainProxy.WebFilterChainDecorator;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.web.reactive.result.view.AbstractView; import org.springframework.web.reactive.result.view.AbstractView;
@ -57,7 +57,7 @@ class WebFluxSecurityConfiguration {
private List<SecurityWebFilterChain> securityWebFilterChains; private List<SecurityWebFilterChain> securityWebFilterChains;
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP; private ObjectPostProcessor<WebFilterChainDecorator> postProcessor = ObjectPostProcessor.identity();
static { static {
isOAuth2Present = ClassUtils.isPresent(REACTIVE_CLIENT_REGISTRATION_REPOSITORY_CLASSNAME, isOAuth2Present = ClassUtils.isPresent(REACTIVE_CLIENT_REGISTRATION_REPOSITORY_CLASSNAME,
@ -73,17 +73,16 @@ class WebFluxSecurityConfiguration {
} }
@Autowired(required = false) @Autowired(required = false)
void setObservationRegistry(ObservationRegistry observationRegistry) { void setFilterChainPostProcessor(ObjectPostProcessor<WebFilterChainDecorator> postProcessor) {
this.observationRegistry = observationRegistry; this.postProcessor = postProcessor;
} }
@Bean(SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME) @Bean(SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME)
@Order(WEB_FILTER_CHAIN_FILTER_ORDER) @Order(WEB_FILTER_CHAIN_FILTER_ORDER)
WebFilterChainProxy springSecurityWebFilterChainFilter() { WebFilterChainProxy springSecurityWebFilterChainFilter() {
WebFilterChainProxy proxy = new WebFilterChainProxy(getSecurityWebFilterChains()); WebFilterChainProxy proxy = new WebFilterChainProxy(getSecurityWebFilterChains());
if (!this.observationRegistry.isNoop()) { WebFilterChainDecorator decorator = this.postProcessor.postProcess(new DefaultWebFilterChainDecorator());
proxy.setFilterChainDecorator(new ObservationWebFilterChainDecorator(this.observationRegistry)); proxy.setFilterChainDecorator(decorator);
}
return proxy; return proxy;
} }

View File

@ -52,7 +52,7 @@ import org.springframework.context.annotation.Import;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Documented @Documented
@Import(WebSocketMessageBrokerSecurityConfiguration.class) @Import({ WebSocketMessageBrokerSecurityConfiguration.class, WebSocketObservationImportSelector.class })
public @interface EnableWebSocketSecurity { public @interface EnableWebSocketSecurity {
} }

View File

@ -20,8 +20,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -33,8 +31,8 @@ import org.springframework.messaging.handler.invocation.HandlerMethodArgumentRes
import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.ObservationAuthorizationManager;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher; import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults; import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.context.SecurityContextHolderStrategy;
@ -79,7 +77,7 @@ final class WebSocketMessageBrokerSecurityConfiguration
private AuthorizationManager<Message<?>> authorizationManager = ANY_MESSAGE_AUTHENTICATED; private AuthorizationManager<Message<?>> authorizationManager = ANY_MESSAGE_AUTHENTICATED;
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP; private ObjectPostProcessor<AuthorizationManager<Message<?>>> postProcessor = ObjectPostProcessor.identity();
private ApplicationContext context; private ApplicationContext context;
@ -106,9 +104,7 @@ final class WebSocketMessageBrokerSecurityConfiguration
} }
AuthorizationManager<Message<?>> manager = this.authorizationManager; AuthorizationManager<Message<?>> manager = this.authorizationManager;
if (!this.observationRegistry.isNoop()) { manager = this.postProcessor.postProcess(manager);
manager = new ObservationAuthorizationManager<>(this.observationRegistry, manager);
}
AuthorizationChannelInterceptor interceptor = new AuthorizationChannelInterceptor(manager); AuthorizationChannelInterceptor interceptor = new AuthorizationChannelInterceptor(manager);
interceptor.setAuthorizationEventPublisher(new SpringAuthorizationEventPublisher(this.context)); interceptor.setAuthorizationEventPublisher(new SpringAuthorizationEventPublisher(this.context));
interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy); interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
@ -128,8 +124,9 @@ final class WebSocketMessageBrokerSecurityConfiguration
} }
@Autowired(required = false) @Autowired(required = false)
void setObservationRegistry(ObservationRegistry observationRegistry) { void setMessageAuthorizationManagerPostProcessor(
this.observationRegistry = observationRegistry; ObjectPostProcessor<AuthorizationManager<Message<?>>> postProcessor) {
this.postProcessor = postProcessor;
} }
@Autowired(required = false) @Autowired(required = false)

View File

@ -0,0 +1,50 @@
/*
* Copyright 2002-2013 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.config.annotation.web.socket;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
/**
* Used by {@link EnableWebSocketSecurity} to conditionally import observation
* configuration when {@link ObservationRegistry} is present.
*
* @author Josh Cummings
* @since 6.4
*/
class WebSocketObservationImportSelector implements ImportSelector {
private static final boolean observabilityPresent;
static {
ClassLoader classLoader = WebSocketObservationImportSelector.class.getClassLoader();
observabilityPresent = ClassUtils.isPresent("io.micrometer.observation.ObservationRegistry", classLoader);
}
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
if (!observabilityPresent) {
return new String[0];
}
return new String[] {
"org.springframework.security.config.annotation.observation.configuration.WebSocketObservationConfiguration" };
}
}

View File

@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -33,13 +34,13 @@ import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.util.context.Context; import reactor.util.context.Context;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
@ -55,9 +56,9 @@ import org.springframework.security.authentication.ReactiveAuthenticationManager
import org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager; import org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager;
import org.springframework.security.authorization.AuthorityReactiveAuthorizationManager; import org.springframework.security.authorization.AuthorityReactiveAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
import org.springframework.security.authorization.ReactiveAuthorizationManager; import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.config.Customizer; import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
@ -1740,6 +1741,18 @@ public class ServerHttpSecurity {
return this.context.getBeanProvider(beanClass).getIfUnique(() -> defaultInstance); return this.context.getBeanProvider(beanClass).getIfUnique(() -> defaultInstance);
} }
private <T> ObjectProvider<T> getBeanProvider(ResolvableType type) {
if (this.context == null) {
return new ObjectProvider<>() {
@Override
public Iterator<T> iterator() {
return Collections.emptyIterator();
}
};
}
return this.context.getBeanProvider(type);
}
private <T> T getBeanOrNull(Class<T> beanClass) { private <T> T getBeanOrNull(Class<T> beanClass) {
return getBeanOrNull(ResolvableType.forClass(beanClass)); return getBeanOrNull(ResolvableType.forClass(beanClass));
} }
@ -1795,6 +1808,17 @@ public class ServerHttpSecurity {
private PathPatternParser pathPatternParser; private PathPatternParser pathPatternParser;
private ObjectPostProcessor<ReactiveAuthorizationManager<ServerWebExchange>> postProcessor = ObjectPostProcessor
.identity();
public AuthorizeExchangeSpec() {
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,
ResolvableType.forClassWithGenerics(ReactiveAuthorizationManager.class, ServerWebExchange.class));
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<ServerWebExchange>>> postProcessor = getBeanProvider(
type);
postProcessor.ifUnique((p) -> this.postProcessor = p);
}
/** /**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity} * Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring * @return the {@link ServerHttpSecurity} to continue configuring
@ -1847,10 +1871,7 @@ public class ServerHttpSecurity {
Assert.state(this.matcher == null, Assert.state(this.matcher == null,
() -> "The matcher " + this.matcher + " does not have an access rule defined"); () -> "The matcher " + this.matcher + " does not have an access rule defined");
ReactiveAuthorizationManager<ServerWebExchange> manager = this.managerBldr.build(); ReactiveAuthorizationManager<ServerWebExchange> manager = this.managerBldr.build();
ObservationRegistry registry = getBeanOrDefault(ObservationRegistry.class, ObservationRegistry.NOOP); manager = this.postProcessor.postProcess(manager);
if (!registry.isNoop()) {
manager = new ObservationReactiveAuthorizationManager<>(registry, manager);
}
AuthorizationWebFilter result = new AuthorizationWebFilter(manager); AuthorizationWebFilter result = new AuthorizationWebFilter(manager);
http.addFilterAt(result, SecurityWebFiltersOrder.AUTHORIZATION); http.addFilterAt(result, SecurityWebFiltersOrder.AUTHORIZATION);
} }