Skip to content

Commit d664967

Browse files
committed
Propagate exceptions in security matchers
Update `ApplicationContextRequestMatcher` and `ApplicationContextServerWebExchangeMatcher` to use a supplier for the context, rather than the context itself. This allow exceptions to be propagated to subclasses which may choose to deal with them. See gh-12238
1 parent 802cd85 commit d664967

File tree

8 files changed

+83
-82
lines changed

8 files changed

+83
-82
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.List;
2424
import java.util.Objects;
2525
import java.util.Set;
26+
import java.util.function.Supplier;
2627
import java.util.stream.Collectors;
2728
import java.util.stream.Stream;
2829

@@ -132,20 +133,20 @@ EndpointServerWebExchangeMatcher excluding(String... endpoints) {
132133
}
133134

134135
@Override
135-
protected void initialized(PathMappedEndpoints pathMappedEndpoints) {
136+
protected void initialized(Supplier<PathMappedEndpoints> pathMappedEndpoints) {
136137
Set<String> paths = new LinkedHashSet<>();
137138
if (this.includes.isEmpty()) {
138-
paths.addAll(pathMappedEndpoints.getAllPaths());
139+
paths.addAll(pathMappedEndpoints.get().getAllPaths());
139140
}
140141
streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add);
141142
streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove);
142143
this.delegate = new OrServerWebExchangeMatcher(getDelegateMatchers(paths));
143144
}
144145

145146
private Stream<String> streamPaths(List<Object> source,
146-
PathMappedEndpoints pathMappedEndpoints) {
147+
Supplier<PathMappedEndpoints> pathMappedEndpoints) {
147148
return source.stream().filter(Objects::nonNull).map(this::getEndpointId)
148-
.map(pathMappedEndpoints::getPath);
149+
.map(pathMappedEndpoints.get()::getPath);
149150
}
150151

151152
private String getEndpointId(Object source) {
@@ -173,7 +174,7 @@ private List<ServerWebExchangeMatcher> getDelegateMatchers(Set<String> paths) {
173174

174175
@Override
175176
protected Mono<MatchResult> matches(ServerWebExchange exchange,
176-
PathMappedEndpoints context) {
177+
Supplier<PathMappedEndpoints> context) {
177178
return this.delegate.matches(exchange);
178179
}
179180

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.List;
2424
import java.util.Objects;
2525
import java.util.Set;
26+
import java.util.function.Supplier;
2627
import java.util.stream.Collectors;
2728
import java.util.stream.Stream;
2829

@@ -129,13 +130,13 @@ EndpointRequestMatcher excluding(String... endpoints) {
129130
}
130131

131132
@Override
132-
protected void initialized(PathMappedEndpoints pathMappedEndpoints) {
133+
protected void initialized(Supplier<PathMappedEndpoints> pathMappedEndpoints) {
133134
Set<String> paths = new LinkedHashSet<>();
134135
if (this.includes.isEmpty()) {
135-
paths.addAll(pathMappedEndpoints.getAllPaths());
136+
paths.addAll(pathMappedEndpoints.get().getAllPaths());
136137
}
137-
streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add);
138-
streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove);
138+
streamPaths(this.includes, pathMappedEndpoints.get()).forEach(paths::add);
139+
streamPaths(this.excludes, pathMappedEndpoints.get()).forEach(paths::remove);
139140
this.delegate = new OrRequestMatcher(getDelegateMatchers(paths));
140141
}
141142

@@ -169,7 +170,7 @@ private List<RequestMatcher> getDelegateMatchers(Set<String> paths) {
169170

170171
@Override
171172
protected boolean matches(HttpServletRequest request,
172-
PathMappedEndpoints context) {
173+
Supplier<PathMappedEndpoints> context) {
173174
return this.delegate.matches(request);
174175
}
175176

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/PathRequest.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.autoconfigure.security.servlet;
1818

19+
import java.util.function.Supplier;
20+
1921
import javax.servlet.http.HttpServletRequest;
2022

2123
import org.springframework.boot.autoconfigure.h2.H2ConsoleProperties;
@@ -69,14 +71,14 @@ private H2ConsoleRequestMatcher() {
6971
}
7072

7173
@Override
72-
protected void initialized(H2ConsoleProperties h2ConsoleProperties) {
74+
protected void initialized(Supplier<H2ConsoleProperties> h2ConsoleProperties) {
7375
this.delegate = new AntPathRequestMatcher(
74-
h2ConsoleProperties.getPath() + "/**");
76+
h2ConsoleProperties.get().getPath() + "/**");
7577
}
7678

7779
@Override
7880
protected boolean matches(HttpServletRequest request,
79-
H2ConsoleProperties context) {
81+
Supplier<H2ConsoleProperties> context) {
8082
return this.delegate.matches(request);
8183
}
8284

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/servlet/StaticResourceRequest.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.LinkedHashSet;
2121
import java.util.List;
2222
import java.util.Set;
23+
import java.util.function.Supplier;
2324
import java.util.stream.Collectors;
2425
import java.util.stream.Stream;
2526

@@ -133,8 +134,9 @@ public StaticResourceRequestMatcher excluding(
133134
}
134135

135136
@Override
136-
protected void initialized(ServerProperties serverProperties) {
137-
this.delegate = new OrRequestMatcher(getDelegateMatchers(serverProperties));
137+
protected void initialized(Supplier<ServerProperties> serverProperties) {
138+
this.delegate = new OrRequestMatcher(
139+
getDelegateMatchers(serverProperties.get()));
138140
}
139141

140142
private List<RequestMatcher> getDelegateMatchers(
@@ -149,7 +151,8 @@ private Stream<String> getPatterns(ServerProperties serverProperties) {
149151
}
150152

151153
@Override
152-
protected boolean matches(HttpServletRequest request, ServerProperties context) {
154+
protected boolean matches(HttpServletRequest request,
155+
Supplier<ServerProperties> context) {
153156
return this.delegate.matches(request);
154157
}
155158

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/reactive/ApplicationContextServerWebExchangeMatcher.java

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616

1717
package org.springframework.boot.security.reactive;
1818

19+
import java.util.function.Supplier;
20+
1921
import reactor.core.publisher.Mono;
2022

21-
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2223
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
2324
import org.springframework.context.ApplicationContext;
2425
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
@@ -32,9 +33,8 @@
3233
* that is autowired in the usual way.
3334
*
3435
* @param <C> The type of the context that the match method actually needs to use. Can be
35-
* an {@link ApplicationContext}, a class of an {@link ApplicationContext#getBean(Class)
36-
* existing bean} or a custom type that will be
37-
* {@link AutowireCapableBeanFactory#createBean(Class, int, boolean) created} on demand.
36+
* an {@link ApplicationContext} or a class of an {@link ApplicationContext#getBean(Class)
37+
* existing bean}.
3838
* @author Madhura Bhave
3939
* @since 2.0.0
4040
*/
@@ -43,7 +43,7 @@ public abstract class ApplicationContextServerWebExchangeMatcher<C>
4343

4444
private final Class<? extends C> contextClass;
4545

46-
private volatile C context;
46+
private volatile Supplier<C> context;
4747

4848
private final Object contextLock = new Object();
4949

@@ -60,12 +60,13 @@ public final Mono<MatchResult> matches(ServerWebExchange exchange) {
6060
/**
6161
* Decides whether the rule implemented by the strategy matches the supplied exchange.
6262
* @param exchange the source exchange
63-
* @param context the context instance
63+
* @param context a supplier for the initialized context (may throw an exception)
6464
* @return if the exchange matches
6565
*/
66-
protected abstract Mono<MatchResult> matches(ServerWebExchange exchange, C context);
66+
protected abstract Mono<MatchResult> matches(ServerWebExchange exchange,
67+
Supplier<C> context);
6768

68-
protected C getContext(ServerWebExchange exchange) {
69+
protected Supplier<C> getContext(ServerWebExchange exchange) {
6970
if (this.context == null) {
7071
synchronized (this.contextLock) {
7172
if (this.context == null) {
@@ -79,26 +80,19 @@ protected C getContext(ServerWebExchange exchange) {
7980

8081
/**
8182
* Called once the context has been initialized.
82-
* @param context the initialized context
83+
* @param context a supplier for the initialized context (may throw an exception)
8384
*/
84-
protected void initialized(C context) {
85+
protected void initialized(Supplier<C> context) {
8586
}
8687

8788
@SuppressWarnings("unchecked")
88-
private C createContext(ServerWebExchange exchange) {
89+
private Supplier<C> createContext(ServerWebExchange exchange) {
8990
ApplicationContext context = exchange.getApplicationContext();
9091
Assert.state(context != null, "No WebApplicationContext found.");
9192
if (this.contextClass.isInstance(context)) {
92-
return (C) context;
93-
}
94-
try {
95-
return context.getBean(this.contextClass);
96-
}
97-
catch (NoSuchBeanDefinitionException ex) {
98-
return (C) context.getAutowireCapableBeanFactory().createBean(
99-
this.contextClass, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR,
100-
false);
93+
return () -> (C) context;
10194
}
95+
return () -> context.getBean(this.contextClass);
10296
}
10397

10498
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/security/servlet/ApplicationContextRequestMatcher.java

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616

1717
package org.springframework.boot.security.servlet;
1818

19+
import java.util.function.Supplier;
20+
1921
import javax.servlet.http.HttpServletRequest;
2022

21-
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2223
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
2324
import org.springframework.context.ApplicationContext;
2425
import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -33,17 +34,16 @@
3334
* that is autowired in the usual way.
3435
*
3536
* @param <C> The type of the context that the match method actually needs to use. Can be
36-
* an {@link ApplicationContext}, a class of an {@link ApplicationContext#getBean(Class)
37-
* existing bean} or a custom type that will be
38-
* {@link AutowireCapableBeanFactory#createBean(Class, int, boolean) created} on demand.
37+
* an {@link ApplicationContext} or a class of an {@link ApplicationContext#getBean(Class)
38+
* existing bean}.
3939
* @author Phillip Webb
4040
* @since 2.0.0
4141
*/
4242
public abstract class ApplicationContextRequestMatcher<C> implements RequestMatcher {
4343

4444
private final Class<? extends C> contextClass;
4545

46-
private volatile C context;
46+
private volatile Supplier<C> context;
4747

4848
private final Object contextLock = new Object();
4949

@@ -60,12 +60,12 @@ public final boolean matches(HttpServletRequest request) {
6060
/**
6161
* Decides whether the rule implemented by the strategy matches the supplied request.
6262
* @param request the source request
63-
* @param context the context instance
63+
* @param context a supplier for the initialized context (may throw an exception)
6464
* @return if the request matches
6565
*/
66-
protected abstract boolean matches(HttpServletRequest request, C context);
66+
protected abstract boolean matches(HttpServletRequest request, Supplier<C> context);
6767

68-
private C getContext(HttpServletRequest request) {
68+
private Supplier<C> getContext(HttpServletRequest request) {
6969
if (this.context == null) {
7070
synchronized (this.contextLock) {
7171
if (this.context == null) {
@@ -79,26 +79,19 @@ private C getContext(HttpServletRequest request) {
7979

8080
/**
8181
* Called once the context has been initialized.
82-
* @param context the initialized context
82+
* @param context a supplier for the initialized context (may throw an exception)
8383
*/
84-
protected void initialized(C context) {
84+
protected void initialized(Supplier<C> context) {
8585
}
8686

8787
@SuppressWarnings("unchecked")
88-
private C createContext(HttpServletRequest request) {
88+
private Supplier<C> createContext(HttpServletRequest request) {
8989
WebApplicationContext context = WebApplicationContextUtils
9090
.getRequiredWebApplicationContext(request.getServletContext());
9191
if (this.contextClass.isInstance(context)) {
92-
return (C) context;
93-
}
94-
try {
95-
return context.getBean(this.contextClass);
96-
}
97-
catch (NoSuchBeanDefinitionException ex) {
98-
return (C) context.getAutowireCapableBeanFactory().createBean(
99-
this.contextClass, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR,
100-
false);
92+
return () -> (C) context;
10193
}
94+
return () -> context.getBean(this.contextClass);
10295
}
10396

10497
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/security/reactive/ApplicationContextServerWebExchangeMatcherTests.java

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616

1717
package org.springframework.boot.security.reactive;
1818

19+
import java.util.function.Supplier;
20+
1921
import org.junit.Rule;
2022
import org.junit.Test;
2123
import org.junit.rules.ExpectedException;
2224
import reactor.core.publisher.Mono;
2325

26+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2427
import org.springframework.context.ApplicationContext;
2528
import org.springframework.context.support.StaticApplicationContext;
2629
import org.springframework.http.server.reactive.ServerHttpRequest;
@@ -58,8 +61,8 @@ public void matchesWhenContextClassIsApplicationContextShouldProvideContext() {
5861
StaticApplicationContext context = (StaticApplicationContext) exchange
5962
.getApplicationContext();
6063
assertThat(new TestApplicationContextServerWebExchangeMatcher<>(
61-
ApplicationContext.class).callMatchesAndReturnProvidedContext(exchange))
62-
.isEqualTo(context);
64+
ApplicationContext.class).callMatchesAndReturnProvidedContext(exchange)
65+
.get()).isEqualTo(context);
6366
}
6467

6568
@Test
@@ -70,19 +73,17 @@ public void matchesWhenContextClassIsExistingBeanShouldProvideBean() {
7073
context.registerSingleton("existingBean", ExistingBean.class);
7174
assertThat(
7275
new TestApplicationContextServerWebExchangeMatcher<>(ExistingBean.class)
73-
.callMatchesAndReturnProvidedContext(exchange))
76+
.callMatchesAndReturnProvidedContext(exchange).get())
7477
.isEqualTo(context.getBean(ExistingBean.class));
7578
}
7679

7780
@Test
78-
public void matchesWhenContextClassIsNewBeanShouldProvideBean() {
81+
public void matchesWhenContextClassIsMissingBeanShouldProvideException() {
7982
ServerWebExchange exchange = createHttpWebHandlerAdapter();
80-
StaticApplicationContext context = (StaticApplicationContext) exchange
81-
.getApplicationContext();
82-
context.registerSingleton("existingBean", ExistingBean.class);
83-
assertThat(new TestApplicationContextServerWebExchangeMatcher<>(NewBean.class)
84-
.callMatchesAndReturnProvidedContext(exchange).getBean())
85-
.isEqualTo(context.getBean(ExistingBean.class));
83+
Supplier<ExistingBean> supplier = new TestApplicationContextServerWebExchangeMatcher<>(
84+
ExistingBean.class).callMatchesAndReturnProvidedContext(exchange);
85+
this.thrown.expect(NoSuchBeanDefinitionException.class);
86+
supplier.get();
8687
}
8788

8889
@Test
@@ -139,24 +140,25 @@ public ExistingBean getBean() {
139140
static class TestApplicationContextServerWebExchangeMatcher<C>
140141
extends ApplicationContextServerWebExchangeMatcher<C> {
141142

142-
private C providedContext;
143+
private Supplier<C> providedContext;
143144

144145
TestApplicationContextServerWebExchangeMatcher(Class<? extends C> context) {
145146
super(context);
146147
}
147148

148-
C callMatchesAndReturnProvidedContext(ServerWebExchange exchange) {
149+
Supplier<C> callMatchesAndReturnProvidedContext(ServerWebExchange exchange) {
149150
matches(exchange);
150151
return getProvidedContext();
151152
}
152153

153154
@Override
154-
protected Mono<MatchResult> matches(ServerWebExchange exchange, C context) {
155+
protected Mono<MatchResult> matches(ServerWebExchange exchange,
156+
Supplier<C> context) {
155157
this.providedContext = context;
156158
return MatchResult.match();
157159
}
158160

159-
C getProvidedContext() {
161+
Supplier<C> getProvidedContext() {
160162
return this.providedContext;
161163
}
162164

0 commit comments

Comments
 (0)