Fix request matcher management context support

Fix caching issues in `ApplicationContextRequestMatcher` and allow
subclasses to ignore an application context entirely. Update existing
matcher implementations so that they deal with the management context
correctly.

Prior to this commit, the `ApplicationContextRequestMatcher` would
return a context cached from the first request. It also didn't
provide any way to ignore a context. This meant that if the user was
running the management server on a different port the matching results
could be inconsistent depending on if the first request arrived on
the regular context or the management context. It also meant that we
could not distinguish between the regular context and the management
context when matching.

Closes gh-18012
This commit is contained in:
Phillip Webb
2019-08-30 14:27:19 -07:00
parent 587e116be8
commit 5938ca78b6
10 changed files with 248 additions and 57 deletions

View File

@@ -23,8 +23,10 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.h2.H2ConsoleProperties;
import org.springframework.boot.autoconfigure.security.StaticResourceLocation;
import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher;
import org.springframework.boot.web.context.WebServerApplicationContext;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.context.WebApplicationContext;
/**
* Factory that can be used to create a {@link RequestMatcher} for commonly used paths.
@@ -69,6 +71,11 @@ public final class PathRequest {
super(H2ConsoleProperties.class);
}
@Override
protected boolean ignoreApplicationContext(WebApplicationContext applicationContext) {
return WebServerApplicationContext.hasServerNamespace(applicationContext, "management");
}
@Override
protected void initialized(Supplier<H2ConsoleProperties> h2ConsoleProperties) {
this.delegate = new AntPathRequestMatcher(h2ConsoleProperties.get().getPath() + "/**");

View File

@@ -29,10 +29,12 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.security.StaticResourceLocation;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher;
import org.springframework.boot.web.context.WebServerApplicationContext;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
/**
* Used to create a {@link RequestMatcher} for static resources in commonly used
@@ -144,6 +146,11 @@ public final class StaticResourceRequest {
.map(dispatcherServletPath::getRelativePath);
}
@Override
protected boolean ignoreApplicationContext(WebApplicationContext applicationContext) {
return WebServerApplicationContext.hasServerNamespace(applicationContext, "management");
}
@Override
protected boolean matches(HttpServletRequest request, Supplier<DispatcherServletPath> context) {
return this.delegate.matches(request);

View File

@@ -27,7 +27,6 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@@ -51,8 +50,20 @@ public class PathRequestTests {
assertMatcher(matcher).doesNotMatch("/js/file.js");
}
@Test
public void toH2ConsoleWhenManagementContextShouldNeverMatch() {
RequestMatcher matcher = PathRequest.toH2Console();
assertMatcher(matcher, "management").doesNotMatch("/h2-console");
assertMatcher(matcher, "management").doesNotMatch("/h2-console/subpath");
assertMatcher(matcher, "management").doesNotMatch("/js/file.js");
}
private RequestMatcherAssert assertMatcher(RequestMatcher matcher) {
StaticWebApplicationContext context = new StaticWebApplicationContext();
return assertMatcher(matcher, null);
}
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String serverNamespace) {
TestWebApplicationContext context = new TestWebApplicationContext(serverNamespace);
context.registerBean(ServerProperties.class);
context.registerBean(H2ConsoleProperties.class);
return assertThat(new RequestMatcherAssert(context, matcher));

View File

@@ -27,7 +27,6 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@@ -53,6 +52,16 @@ public class StaticResourceRequestTests {
assertMatcher(matcher).doesNotMatch("/bar");
}
@Test
public void atCommonLocationsWhenManagementContextShouldNeverMatch() {
RequestMatcher matcher = this.resourceRequest.atCommonLocations();
assertMatcher(matcher, "management").doesNotMatch("/css/file.css");
assertMatcher(matcher, "management").doesNotMatch("/js/file.js");
assertMatcher(matcher, "management").doesNotMatch("/images/file.css");
assertMatcher(matcher, "management").doesNotMatch("/webjars/file.css");
assertMatcher(matcher, "management").doesNotMatch("/foo/favicon.ico");
}
@Test
public void atCommonLocationsWithExcludeShouldNotMatchExcluded() {
RequestMatcher matcher = this.resourceRequest.atCommonLocations().excluding(StaticResourceLocation.CSS);
@@ -70,8 +79,8 @@ public class StaticResourceRequestTests {
@Test
public void atLocationWhenHasServletPathShouldMatchLocation() {
RequestMatcher matcher = this.resourceRequest.at(StaticResourceLocation.CSS);
assertMatcher(matcher, "/foo").matches("/foo", "/css/file.css");
assertMatcher(matcher, "/foo").doesNotMatch("/foo", "/js/file.js");
assertMatcher(matcher, null, "/foo").matches("/foo", "/css/file.css");
assertMatcher(matcher, null, "/foo").doesNotMatch("/foo", "/js/file.js");
}
@Test
@@ -87,15 +96,16 @@ public class StaticResourceRequestTests {
}
private RequestMatcherAssert assertMatcher(RequestMatcher matcher) {
DispatcherServletPath dispatcherServletPath = () -> "";
StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerBean(DispatcherServletPath.class, () -> dispatcherServletPath);
return assertThat(new RequestMatcherAssert(context, matcher));
return assertMatcher(matcher, null, "");
}
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String path) {
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String serverNamespace) {
return assertMatcher(matcher, serverNamespace, "");
}
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String serverNamespace, String path) {
DispatcherServletPath dispatcherServletPath = () -> path;
StaticWebApplicationContext context = new StaticWebApplicationContext();
TestWebApplicationContext context = new TestWebApplicationContext(serverNamespace);
context.registerBean(DispatcherServletPath.class, () -> dispatcherServletPath);
return assertThat(new RequestMatcherAssert(context, matcher));
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2012-2019 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.boot.autoconfigure.security.servlet;
import org.springframework.boot.web.context.WebServerApplicationContext;
import org.springframework.boot.web.server.WebServer;
import org.springframework.web.context.support.StaticWebApplicationContext;
/**
* Test {@link StaticWebApplicationContext} that also implements
* {@link WebServerApplicationContext}.
*
* @author Phillip Webb
*/
class TestWebApplicationContext extends StaticWebApplicationContext implements WebServerApplicationContext {
private final String serverNamespace;
TestWebApplicationContext(String serverNamespace) {
this.serverNamespace = serverNamespace;
}
@Override
public WebServer getWebServer() {
return null;
}
@Override
public String getServerNamespace() {
return this.serverNamespace;
}
}