Skip to content

Commit 69e3c24

Browse files
committed
Abstract ObservationRegistry Behind ObjectPostProcessor
Issue gh-15678
1 parent 1ed20aa commit 69e3c24

27 files changed

+645
-146
lines changed

config/src/main/java/org/springframework/security/config/annotation/ObjectPostProcessor.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@
3131
*/
3232
public interface ObjectPostProcessor<T> {
3333

34+
static <S> ObjectPostProcessor<S> identity() {
35+
return new ObjectPostProcessor<>() {
36+
@Override
37+
public <O extends S> O postProcess(O object) {
38+
return object;
39+
}
40+
};
41+
}
42+
3443
/**
3544
* Initialize the object possibly returning a modified instance that should be used
3645
* instead.

config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.util.function.Supplier;
2020

21-
import io.micrometer.observation.ObservationRegistry;
2221
import org.aopalliance.intercept.MethodInterceptor;
2322
import org.aopalliance.intercept.MethodInvocation;
2423

@@ -36,9 +35,9 @@
3635
import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
3736
import org.springframework.security.authorization.AuthorizationEventPublisher;
3837
import org.springframework.security.authorization.AuthorizationManager;
39-
import org.springframework.security.authorization.ObservationAuthorizationManager;
4038
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
4139
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
40+
import org.springframework.security.config.annotation.ObjectPostProcessor;
4241
import org.springframework.security.config.core.GrantedAuthorityDefaults;
4342
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4443

@@ -58,8 +57,15 @@ final class Jsr250MethodSecurityConfiguration implements ImportAware, AopInfrast
5857

5958
private final Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager();
6059

61-
private AuthorizationManagerBeforeMethodInterceptor methodInterceptor = AuthorizationManagerBeforeMethodInterceptor
62-
.jsr250(this.authorizationManager);
60+
private final AuthorizationManagerBeforeMethodInterceptor methodInterceptor;
61+
62+
Jsr250MethodSecurityConfiguration(
63+
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> postProcessors) {
64+
ObjectPostProcessor<AuthorizationManager<MethodInvocation>> postProcessor = postProcessors
65+
.getIfUnique(ObjectPostProcessor::identity);
66+
AuthorizationManager<MethodInvocation> manager = postProcessor.postProcess(this.authorizationManager);
67+
this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.jsr250(manager);
68+
}
6369

6470
@Bean
6571
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@@ -95,16 +101,6 @@ void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityCont
95101
this.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
96102
}
97103

98-
@Autowired(required = false)
99-
void setObservationRegistry(ObservationRegistry registry) {
100-
if (registry.isNoop()) {
101-
return;
102-
}
103-
AuthorizationManager<MethodInvocation> observed = new ObservationAuthorizationManager<>(registry,
104-
this.authorizationManager);
105-
this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.secured(observed);
106-
}
107-
108104
@Autowired(required = false)
109105
void setEventPublisher(AuthorizationEventPublisher eventPublisher) {
110106
this.methodInterceptor.setAuthorizationEventPublisher(eventPublisher);

config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ final class MethodSecuritySelector implements ImportSelector {
4141
private static final boolean isDataPresent = ClassUtils
4242
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
4343

44+
private static final boolean isObservabilityPresent = ClassUtils
45+
.isPresent("io.micrometer.observation.ObservationRegistry", null);
46+
4447
private final ImportSelector autoProxy = new AutoProxyRegistrarSelector();
4548

4649
@Override
@@ -64,6 +67,10 @@ public String[] selectImports(@NonNull AnnotationMetadata importMetadata) {
6467
if (isDataPresent) {
6568
imports.add(AuthorizationProxyDataConfiguration.class.getName());
6669
}
70+
if (isObservabilityPresent) {
71+
imports.add(
72+
"org.springframework.security.config.annotation.observation.configuration.ObservationConfiguration");
73+
}
6774
return imports.toArray(new String[0]);
6875
}
6976

config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
package org.springframework.security.config.annotation.method.configuration;
1818

19-
import io.micrometer.observation.ObservationRegistry;
2019
import org.aopalliance.intercept.MethodInterceptor;
20+
import org.aopalliance.intercept.MethodInvocation;
2121

2222
import org.springframework.aop.Pointcut;
2323
import org.springframework.aop.framework.AopInfrastructureBean;
@@ -38,14 +38,16 @@
3838
import org.springframework.security.aot.hint.PrePostAuthorizeHintsRegistrar;
3939
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
4040
import org.springframework.security.authorization.AuthorizationEventPublisher;
41-
import org.springframework.security.authorization.ObservationAuthorizationManager;
41+
import org.springframework.security.authorization.AuthorizationManager;
4242
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
4343
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
44+
import org.springframework.security.authorization.method.MethodInvocationResult;
4445
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
4546
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
4647
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
4748
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
4849
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
50+
import org.springframework.security.config.annotation.ObjectPostProcessor;
4951
import org.springframework.security.config.core.GrantedAuthorityDefaults;
5052
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
5153
import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -78,21 +80,29 @@ final class PrePostMethodSecurityConfiguration implements ImportAware, Applicati
7880

7981
private final PreFilterAuthorizationMethodInterceptor preFilterMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
8082

81-
private AuthorizationManagerBeforeMethodInterceptor preAuthorizeMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor
82-
.preAuthorize(this.preAuthorizeAuthorizationManager);
83+
private final AuthorizationManagerBeforeMethodInterceptor preAuthorizeMethodInterceptor;
8384

84-
private AuthorizationManagerAfterMethodInterceptor postAuthorizeMethodInterceptor = AuthorizationManagerAfterMethodInterceptor
85-
.postAuthorize(this.postAuthorizeAuthorizationManager);
85+
private final AuthorizationManagerAfterMethodInterceptor postAuthorizeMethodInterceptor;
8686

8787
private final PostFilterAuthorizationMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationMethodInterceptor();
8888

8989
private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
9090

91-
{
91+
PrePostMethodSecurityConfiguration(
92+
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> preAuthorizeProcessor,
93+
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocationResult>>> postAuthorizeProcessor) {
9294
this.preFilterMethodInterceptor.setExpressionHandler(this.expressionHandler);
9395
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
9496
this.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
9597
this.postFilterMethodInterceptor.setExpressionHandler(this.expressionHandler);
98+
AuthorizationManager<MethodInvocation> preAuthorize = preAuthorizeProcessor
99+
.getIfUnique(ObjectPostProcessor::identity)
100+
.postProcess(this.preAuthorizeAuthorizationManager);
101+
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor.preAuthorize(preAuthorize);
102+
AuthorizationManager<MethodInvocationResult> postAuthorize = postAuthorizeProcessor
103+
.getIfUnique(ObjectPostProcessor::identity)
104+
.postProcess(this.postAuthorizeAuthorizationManager);
105+
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterMethodInterceptor.postAuthorize(postAuthorize);
96106
}
97107

98108
@Override
@@ -144,17 +154,6 @@ void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityCont
144154
this.postFilterMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
145155
}
146156

147-
@Autowired(required = false)
148-
void setObservationRegistry(ObservationRegistry registry) {
149-
if (registry.isNoop()) {
150-
return;
151-
}
152-
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor
153-
.preAuthorize(new ObservationAuthorizationManager<>(registry, this.preAuthorizeAuthorizationManager));
154-
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterMethodInterceptor
155-
.postAuthorize(new ObservationAuthorizationManager<>(registry, this.postAuthorizeAuthorizationManager));
156-
}
157-
158157
@Autowired(required = false)
159158
void setAuthorizationEventPublisher(AuthorizationEventPublisher publisher) {
160159
this.preAuthorizeMethodInterceptor.setAuthorizationEventPublisher(publisher);

config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveAuthorizationManagerMethodSecurityConfiguration.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
package org.springframework.security.config.annotation.method.configuration;
1818

19-
import io.micrometer.observation.ObservationRegistry;
2019
import org.aopalliance.intercept.MethodInterceptor;
20+
import org.aopalliance.intercept.MethodInvocation;
2121

2222
import org.springframework.aop.Pointcut;
2323
import org.springframework.aop.framework.AopInfrastructureBean;
@@ -34,14 +34,16 @@
3434
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
3535
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
3636
import org.springframework.security.authentication.ReactiveAuthenticationManager;
37-
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
37+
import org.springframework.security.authorization.ReactiveAuthorizationManager;
3838
import org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor;
3939
import org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor;
40+
import org.springframework.security.authorization.method.MethodInvocationResult;
4041
import org.springframework.security.authorization.method.PostAuthorizeReactiveAuthorizationManager;
4142
import org.springframework.security.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor;
4243
import org.springframework.security.authorization.method.PreAuthorizeReactiveAuthorizationManager;
4344
import org.springframework.security.authorization.method.PreFilterAuthorizationReactiveMethodInterceptor;
4445
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
46+
import org.springframework.security.config.annotation.ObjectPostProcessor;
4547
import org.springframework.security.config.core.GrantedAuthorityDefaults;
4648
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
4749

@@ -77,22 +79,30 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration
7779

7880
private PostFilterAuthorizationReactiveMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor();
7981

80-
private AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeMethodInterceptor;
82+
private final AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeMethodInterceptor;
8183

82-
private AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;
84+
private final AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;
8385

8486
@Autowired(required = false)
85-
ReactiveAuthorizationManagerMethodSecurityConfiguration(MethodSecurityExpressionHandler expressionHandler) {
87+
ReactiveAuthorizationManagerMethodSecurityConfiguration(MethodSecurityExpressionHandler expressionHandler,
88+
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>>> preAuthorizePostProcessor,
89+
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>>> postAuthorizePostProcessor) {
8690
if (expressionHandler != null) {
8791
this.preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
8892
this.preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager(expressionHandler);
8993
this.postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
9094
this.postAuthorizeAuthorizationManager = new PostAuthorizeReactiveAuthorizationManager(expressionHandler);
9195
}
96+
ReactiveAuthorizationManager<MethodInvocation> preAuthorize = preAuthorizePostProcessor
97+
.getIfUnique(ObjectPostProcessor::identity)
98+
.postProcess(this.preAuthorizeAuthorizationManager);
9299
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeReactiveMethodInterceptor
93-
.preAuthorize(this.preAuthorizeAuthorizationManager);
100+
.preAuthorize(preAuthorize);
101+
ReactiveAuthorizationManager<MethodInvocationResult> postAuthorize = postAuthorizePostProcessor
102+
.getIfAvailable(ObjectPostProcessor::identity)
103+
.postProcess(this.postAuthorizeAuthorizationManager);
94104
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterReactiveMethodInterceptor
95-
.postAuthorize(this.postAuthorizeAuthorizationManager);
105+
.postAuthorize(postAuthorize);
96106
}
97107

98108
@Override
@@ -117,17 +127,6 @@ void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults)
117127
this.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
118128
}
119129

120-
@Autowired(required = false)
121-
void setObservationRegistry(ObservationRegistry registry) {
122-
if (registry.isNoop()) {
123-
return;
124-
}
125-
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(
126-
new ObservationReactiveAuthorizationManager<>(registry, this.preAuthorizeAuthorizationManager));
127-
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(
128-
new ObservationReactiveAuthorizationManager<>(registry, this.postAuthorizeAuthorizationManager));
129-
}
130-
131130
@Bean
132131
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
133132
static MethodInterceptor preFilterAuthorizationMethodInterceptor(

config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecuritySelector.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ class ReactiveMethodSecuritySelector implements ImportSelector {
3838
private static final boolean isDataPresent = ClassUtils
3939
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
4040

41+
private static final boolean isObservabilityPresent = ClassUtils
42+
.isPresent("io.micrometer.observation.ObservationRegistry", null);
43+
4144
private final ImportSelector autoProxy = new AutoProxyRegistrarSelector();
4245

4346
@Override
@@ -58,6 +61,10 @@ public String[] selectImports(AnnotationMetadata importMetadata) {
5861
if (isDataPresent) {
5962
imports.add(AuthorizationProxyDataConfiguration.class.getName());
6063
}
64+
if (isObservabilityPresent) {
65+
imports.add(
66+
"org.springframework.security.config.annotation.observation.configuration.ReactiveObservationConfiguration");
67+
}
6168
imports.add(AuthorizationProxyConfiguration.class.getName());
6269
return imports.toArray(new String[0]);
6370
}

config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.util.function.Supplier;
2020

21-
import io.micrometer.observation.ObservationRegistry;
2221
import org.aopalliance.intercept.MethodInterceptor;
2322
import org.aopalliance.intercept.MethodInvocation;
2423

@@ -37,9 +36,9 @@
3736
import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
3837
import org.springframework.security.authorization.AuthorizationEventPublisher;
3938
import org.springframework.security.authorization.AuthorizationManager;
40-
import org.springframework.security.authorization.ObservationAuthorizationManager;
4139
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
4240
import org.springframework.security.authorization.method.SecuredAuthorizationManager;
41+
import org.springframework.security.config.annotation.ObjectPostProcessor;
4342
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4443

4544
/**
@@ -58,8 +57,15 @@ final class SecuredMethodSecurityConfiguration implements ImportAware, AopInfras
5857

5958
private final SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager();
6059

61-
private AuthorizationManagerBeforeMethodInterceptor methodInterceptor = AuthorizationManagerBeforeMethodInterceptor
62-
.secured(this.authorizationManager);
60+
private final AuthorizationManagerBeforeMethodInterceptor methodInterceptor;
61+
62+
SecuredMethodSecurityConfiguration(
63+
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> postProcessors) {
64+
ObjectPostProcessor<AuthorizationManager<MethodInvocation>> postProcessor = postProcessors
65+
.getIfUnique(ObjectPostProcessor::identity);
66+
AuthorizationManager<MethodInvocation> manager = postProcessor.postProcess(this.authorizationManager);
67+
this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.secured(manager);
68+
}
6369

6470
@Bean
6571
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@@ -90,16 +96,6 @@ void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityCont
9096
this.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
9197
}
9298

93-
@Autowired(required = false)
94-
void setObservationRegistry(ObservationRegistry registry) {
95-
if (registry.isNoop()) {
96-
return;
97-
}
98-
AuthorizationManager<MethodInvocation> observed = new ObservationAuthorizationManager<>(registry,
99-
this.authorizationManager);
100-
this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.secured(observed);
101-
}
102-
10399
@Autowired(required = false)
104100
void setEventPublisher(AuthorizationEventPublisher eventPublisher) {
105101
this.methodInterceptor.setAuthorizationEventPublisher(eventPublisher);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.observation.configuration;
18+
19+
import java.util.function.BiFunction;
20+
import java.util.function.Function;
21+
22+
import io.micrometer.observation.ObservationRegistry;
23+
24+
import org.springframework.beans.factory.ObjectProvider;
25+
import org.springframework.security.config.annotation.ObjectPostProcessor;
26+
27+
abstract class AbstractObservationObjectPostProcessor<O> implements ObjectPostProcessor<O> {
28+
29+
private final ObjectProvider<ObservationRegistry> observationRegistry;
30+
31+
private final BiFunction<ObservationRegistry, O, O> wrapper;
32+
33+
AbstractObservationObjectPostProcessor(ObjectProvider<ObservationRegistry> observationRegistry,
34+
Function<ObservationRegistry, O> constructor) {
35+
this(observationRegistry, (registry, object) -> constructor.apply(registry));
36+
}
37+
38+
AbstractObservationObjectPostProcessor(ObjectProvider<ObservationRegistry> observationRegistry,
39+
BiFunction<ObservationRegistry, O, O> constructor) {
40+
this.observationRegistry = observationRegistry;
41+
this.wrapper = constructor;
42+
}
43+
44+
@Override
45+
public <O1 extends O> O1 postProcess(O1 object) {
46+
ObservationRegistry registry = this.observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP);
47+
if (registry.isNoop()) {
48+
return object;
49+
}
50+
return (O1) this.wrapper.apply(registry, object);
51+
}
52+
53+
}

0 commit comments

Comments
 (0)