mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Rework security request matchers
Update the security request matchers so that a bean is no longer needed when the matcher is used. Matchers can now be build by starting from the `EndpointRequest` or `StaticResourceRequest` classes. For example: http.authorizeRequests() .requestMatchers(EndpointRequest.to("status", "info")).permitAll() .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR") .requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll() Closes gh-7958
This commit is contained in:
parent
2e51b48cd9
commit
46dfe38b60
@ -33,7 +33,7 @@ import org.springframework.core.env.Environment;
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class EndpointProvider<T extends Operation> {
|
||||
public class EndpointProvider<T extends Operation> {
|
||||
|
||||
private final EndpointDiscoverer<T> discoverer;
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.actuate.autoconfigure.endpoint.web;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointInfo;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default {@link EndpointPathProvider} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class DefaultEndpointPathProvider implements EndpointPathProvider {
|
||||
|
||||
private final Collection<EndpointInfo<WebEndpointOperation>> endpoints;
|
||||
|
||||
private final String contextPath;
|
||||
|
||||
public DefaultEndpointPathProvider(EndpointProvider<WebEndpointOperation> provider,
|
||||
ManagementServerProperties managementServerProperties) {
|
||||
this.endpoints = provider.getEndpoints();
|
||||
this.contextPath = managementServerProperties.getContextPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPaths() {
|
||||
return this.endpoints.stream().map(this::getPath).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath(String id) {
|
||||
Assert.notNull(id, "ID must not be null");
|
||||
return this.endpoints.stream().filter((info) -> id.equals(info.getId()))
|
||||
.findFirst().map(this::getPath).orElse(null);
|
||||
}
|
||||
|
||||
private String getPath(EndpointInfo<WebEndpointOperation> endpointInfo) {
|
||||
return this.contextPath + "/" + endpointInfo.getId();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.actuate.autoconfigure.endpoint.web;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface that provides path information for web mapped endpints.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public interface EndpointPathProvider {
|
||||
|
||||
/**
|
||||
* Return all mapped endpoint paths.
|
||||
* @return all paths
|
||||
*/
|
||||
List<String> getPaths();
|
||||
|
||||
/**
|
||||
* Return the path for the endpoint with the specified ID.
|
||||
* @param id the endpoint ID
|
||||
* @return the path of the endpoint or {@code null}
|
||||
*/
|
||||
String getPath(String id);
|
||||
|
||||
}
|
@ -21,6 +21,8 @@ import java.util.HashSet;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.DefaultEndpointPathProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
@ -60,4 +62,12 @@ class JerseyWebEndpointManagementContextConfiguration {
|
||||
provider.getEndpoints())));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EndpointPathProvider endpointPathProvider(
|
||||
EndpointProvider<WebEndpointOperation> provider,
|
||||
ManagementServerProperties managementServerProperties) {
|
||||
return new DefaultEndpointPathProvider(provider, managementServerProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.DefaultEndpointPathProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
@ -49,4 +51,12 @@ public class WebFluxEndpointManagementContextConfiguration {
|
||||
provider.getEndpoints());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EndpointPathProvider endpointPathProvider(
|
||||
EndpointProvider<WebEndpointOperation> provider,
|
||||
ManagementServerProperties managementServerProperties) {
|
||||
return new DefaultEndpointPathProvider(provider, managementServerProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.DefaultEndpointPathProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
@ -61,6 +63,14 @@ public class WebMvcEndpointManagementContextConfiguration {
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EndpointPathProvider endpointPathProvider(
|
||||
EndpointProvider<WebEndpointOperation> provider,
|
||||
ManagementServerProperties managementServerProperties) {
|
||||
return new DefaultEndpointPathProvider(provider, managementServerProperties);
|
||||
}
|
||||
|
||||
private CorsConfiguration getCorsConfiguration(CorsEndpointProperties properties) {
|
||||
if (CollectionUtils.isEmpty(properties.getAllowedOrigins())) {
|
||||
return null;
|
||||
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.actuate.autoconfigure.security;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.security.ApplicationContextRequestMatcher;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Factory that can be used to create a {@link RequestMatcher} for actuator endpoint
|
||||
* locations.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class EndpointRequest {
|
||||
|
||||
private EndpointRequest() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher that includes all {@link Endpoint actuator endpoints}. The
|
||||
* {@link EndpointRequestMatcher#excluding(Class...) excluding} method can be used to
|
||||
* further remove specific endpoints if required. For example: <pre class="code">
|
||||
* EndpointRequestMatcher.toAnyEndpoint().excluding(ShutdownEndpoint.class)
|
||||
* </pre>
|
||||
* @return the configured {@link RequestMatcher}
|
||||
*/
|
||||
public static EndpointRequestMatcher toAnyEndpoint() {
|
||||
return new EndpointRequestMatcher();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher that includes the specified {@link Endpoint actuator endpoints}.
|
||||
* For example: <pre class="code">
|
||||
* EndpointRequestMatcher.to(ShutdownEndpoint.class, HealthEndpoint.class)
|
||||
* </pre>
|
||||
* @param endpoints the endpoints to include
|
||||
* @return the configured {@link RequestMatcher}
|
||||
*/
|
||||
public static EndpointRequestMatcher to(Class<?>... endpoints) {
|
||||
return new EndpointRequestMatcher(endpoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher that includes the specified {@link Endpoint actuator endpoints}.
|
||||
* For example: <pre class="code">
|
||||
* EndpointRequestMatcher.to("shutdown", "health")
|
||||
* </pre>
|
||||
* @param endpoints the endpoints to include
|
||||
* @return the configured {@link RequestMatcher}
|
||||
*/
|
||||
public static EndpointRequestMatcher to(String... endpoints) {
|
||||
return new EndpointRequestMatcher(endpoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* The request matcher used to match against {@link Endpoint actuator endpoints}.
|
||||
*/
|
||||
public final static class EndpointRequestMatcher
|
||||
extends ApplicationContextRequestMatcher<EndpointPathProvider> {
|
||||
|
||||
private final List<Object> includes;
|
||||
|
||||
private final List<Object> excludes;
|
||||
|
||||
private RequestMatcher delegate;
|
||||
|
||||
private EndpointRequestMatcher() {
|
||||
super(EndpointPathProvider.class);
|
||||
this.includes = Collections.emptyList();
|
||||
this.excludes = Collections.emptyList();
|
||||
}
|
||||
|
||||
private EndpointRequestMatcher(Class<?>[] endpoints) {
|
||||
super(EndpointPathProvider.class);
|
||||
this.includes = Arrays.asList((Object[]) endpoints);
|
||||
this.excludes = Collections.emptyList();
|
||||
}
|
||||
|
||||
private EndpointRequestMatcher(String[] endpoints) {
|
||||
super(EndpointPathProvider.class);
|
||||
this.includes = Arrays.asList((Object[]) endpoints);
|
||||
this.excludes = Collections.emptyList();
|
||||
}
|
||||
|
||||
private EndpointRequestMatcher(List<Object> includes, List<Object> excludes) {
|
||||
super(EndpointPathProvider.class);
|
||||
this.includes = includes;
|
||||
this.excludes = excludes;
|
||||
}
|
||||
|
||||
EndpointRequestMatcher excluding(Class<?>... endpoints) {
|
||||
List<Object> excludes = new ArrayList<>(this.excludes);
|
||||
excludes.addAll(Arrays.asList((Object[]) endpoints));
|
||||
return new EndpointRequestMatcher(this.includes, excludes);
|
||||
}
|
||||
|
||||
EndpointRequestMatcher excluding(String... endpoints) {
|
||||
List<Object> excludes = new ArrayList<>(this.excludes);
|
||||
excludes.addAll(Arrays.asList((Object[]) endpoints));
|
||||
return new EndpointRequestMatcher(this.includes, excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialized(EndpointPathProvider endpointPathProvider) {
|
||||
Set<String> paths = new LinkedHashSet<>(this.includes.isEmpty()
|
||||
? endpointPathProvider.getPaths() : Collections.emptyList());
|
||||
streamPaths(this.includes, endpointPathProvider).forEach(paths::add);
|
||||
streamPaths(this.excludes, endpointPathProvider).forEach(paths::remove);
|
||||
this.delegate = new OrRequestMatcher(getDelegateMatchers(paths));
|
||||
}
|
||||
|
||||
private Stream<String> streamPaths(List<Object> source,
|
||||
EndpointPathProvider endpointPathProvider) {
|
||||
return source.stream().filter(Objects::nonNull).map(this::getPathId)
|
||||
.map(endpointPathProvider::getPath);
|
||||
}
|
||||
|
||||
private String getPathId(Object source) {
|
||||
if (source instanceof String) {
|
||||
return (String) source;
|
||||
}
|
||||
if (source instanceof Class) {
|
||||
return getPathId((Class<?>) source);
|
||||
}
|
||||
throw new IllegalStateException("Unsupported source " + source);
|
||||
}
|
||||
|
||||
private String getPathId(Class<?> source) {
|
||||
Endpoint annotation = AnnotationUtils.findAnnotation(source, Endpoint.class);
|
||||
Assert.state(annotation != null,
|
||||
"Class " + source + " is not annotated with @Endpoint");
|
||||
return annotation.id();
|
||||
}
|
||||
|
||||
private List<RequestMatcher> getDelegateMatchers(Set<String> paths) {
|
||||
return paths.stream().map((path) -> new AntPathRequestMatcher(path + "/**"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
try {
|
||||
return super.matches(request);
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(HttpServletRequest request,
|
||||
EndpointPathProvider context) {
|
||||
return this.delegate.matches(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for security.
|
||||
*/
|
||||
package org.springframework.boot.actuate.autoconfigure.security;
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.actuate.autoconfigure.endpoint.web;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointInfo;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultEndpointPathProvider}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DefaultEndpointPathProviderTests {
|
||||
|
||||
@Mock
|
||||
private EndpointProvider<WebEndpointOperation> endpointProvider;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPathsShouldReturnAllPaths() throws Exception {
|
||||
DefaultEndpointPathProvider provider = createProvider("");
|
||||
assertThat(provider.getPaths()).containsOnly("/foo", "/bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPathsWhenHasContextPathShouldReturnAllPathsWithContext()
|
||||
throws Exception {
|
||||
DefaultEndpointPathProvider provider = createProvider("/application");
|
||||
assertThat(provider.getPaths()).containsOnly("/application/foo",
|
||||
"/application/bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPathWhenEndpointIdIsKnownShouldReturnPath() throws Exception {
|
||||
DefaultEndpointPathProvider provider = createProvider("");
|
||||
assertThat(provider.getPath("foo")).isEqualTo("/foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPathWhenEndpointIdIsUnknownShouldReturnNull() throws Exception {
|
||||
DefaultEndpointPathProvider provider = createProvider("");
|
||||
assertThat(provider.getPath("baz")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPathWhenHasContextPathReturnPath() throws Exception {
|
||||
DefaultEndpointPathProvider provider = createProvider("/application");
|
||||
assertThat(provider.getPath("foo")).isEqualTo("/application/foo");
|
||||
}
|
||||
|
||||
private DefaultEndpointPathProvider createProvider(String contextPath) {
|
||||
Collection<EndpointInfo<WebEndpointOperation>> endpoints = new ArrayList<>();
|
||||
endpoints.add(new EndpointInfo<>("foo", true, Collections.emptyList()));
|
||||
endpoints.add(new EndpointInfo<>("bar", true, Collections.emptyList()));
|
||||
given(this.endpointProvider.getEndpoints()).willReturn(endpoints);
|
||||
ManagementServerProperties managementServerProperties = new ManagementServerProperties();
|
||||
managementServerProperties.setContextPath(contextPath);
|
||||
return new DefaultEndpointPathProvider(this.endpointProvider,
|
||||
managementServerProperties);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.actuate.autoconfigure.security;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.assertj.core.api.AssertDelegateTarget;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests for {@link EndpointRequest}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class EndpointRequestTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void toAnyEndpointShouldMatchEndpointPath() throws Exception {
|
||||
RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
|
||||
assertMatcher(matcher).matches("/application/foo");
|
||||
assertMatcher(matcher).matches("/application/bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toAnyEndpointShouldNotMatchOtherPath() throws Exception {
|
||||
RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
|
||||
assertMatcher(matcher).doesNotMatch("/application/baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toEndpointClassShouldMatchEndpointPath() throws Exception {
|
||||
RequestMatcher matcher = EndpointRequest.to(FooEndpoint.class);
|
||||
assertMatcher(matcher).matches("/application/foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toEndpointClassShouldNotMatchOtherPath() throws Exception {
|
||||
RequestMatcher matcher = EndpointRequest.to(FooEndpoint.class);
|
||||
assertMatcher(matcher).doesNotMatch("/application/bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toEndpointIdShouldMatchEndpointPath() throws Exception {
|
||||
RequestMatcher matcher = EndpointRequest.to("foo");
|
||||
assertMatcher(matcher).matches("/application/foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toEndpointIdShouldNotMatchOtherPath() throws Exception {
|
||||
RequestMatcher matcher = EndpointRequest.to("foo");
|
||||
assertMatcher(matcher).doesNotMatch("/application/bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void excludeByClassShouldNotMatchExcluded() throws Exception {
|
||||
RequestMatcher matcher = EndpointRequest.toAnyEndpoint()
|
||||
.excluding(FooEndpoint.class);
|
||||
assertMatcher(matcher).doesNotMatch("/application/foo");
|
||||
assertMatcher(matcher).matches("/application/bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void excludeByIdShouldNotMatchExcluded() throws Exception {
|
||||
RequestMatcher matcher = EndpointRequest.toAnyEndpoint().excluding("foo");
|
||||
assertMatcher(matcher).doesNotMatch("/application/foo");
|
||||
assertMatcher(matcher).matches("/application/bar");
|
||||
}
|
||||
|
||||
private RequestMatcherAssert assertMatcher(RequestMatcher matcher) {
|
||||
return assertMatcher(matcher, new MockEndpointPathProvider());
|
||||
}
|
||||
|
||||
private RequestMatcherAssert assertMatcher(RequestMatcher matcher,
|
||||
EndpointPathProvider endpointPathProvider) {
|
||||
StaticWebApplicationContext context = new StaticWebApplicationContext();
|
||||
context.registerBean(EndpointPathProvider.class, () -> endpointPathProvider);
|
||||
return assertThat(new RequestMatcherAssert(context, matcher));
|
||||
}
|
||||
|
||||
private static class RequestMatcherAssert implements AssertDelegateTarget {
|
||||
|
||||
private final WebApplicationContext context;
|
||||
|
||||
private final RequestMatcher matcher;
|
||||
|
||||
RequestMatcherAssert(WebApplicationContext context, RequestMatcher matcher) {
|
||||
this.context = context;
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
public void matches(String path) {
|
||||
matches(mockRequest(path));
|
||||
}
|
||||
|
||||
private void matches(HttpServletRequest request) {
|
||||
assertThat(this.matcher.matches(request))
|
||||
.as("Matches " + getRequestPath(request)).isTrue();
|
||||
}
|
||||
|
||||
public void doesNotMatch(String path) {
|
||||
doesNotMatch(mockRequest(path));
|
||||
}
|
||||
|
||||
private void doesNotMatch(HttpServletRequest request) {
|
||||
assertThat(this.matcher.matches(request))
|
||||
.as("Does not match " + getRequestPath(request)).isFalse();
|
||||
}
|
||||
|
||||
private MockHttpServletRequest mockRequest(String path) {
|
||||
return mockRequest(null, path);
|
||||
}
|
||||
|
||||
private MockHttpServletRequest mockRequest(String servletPath, String path) {
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
servletContext.setAttribute(
|
||||
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
|
||||
this.context);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(servletContext);
|
||||
if (servletPath != null) {
|
||||
request.setServletPath(servletPath);
|
||||
}
|
||||
request.setPathInfo(path);
|
||||
return request;
|
||||
}
|
||||
|
||||
private String getRequestPath(HttpServletRequest request) {
|
||||
String url = request.getServletPath();
|
||||
if (request.getPathInfo() != null) {
|
||||
url += request.getPathInfo();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MockEndpointPathProvider implements EndpointPathProvider {
|
||||
|
||||
@Override
|
||||
public List<String> getPaths() {
|
||||
return Arrays.asList("/application/foo", "/application/bar");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath(String id) {
|
||||
if ("foo".equals(id)) {
|
||||
return "/application/foo";
|
||||
}
|
||||
if ("bar".equals(id)) {
|
||||
return "/application/bar";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "foo")
|
||||
private static class FooEndpoint {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.security.ApplicationContextRequestMatcher;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Factory that can be used to create a {@link RequestMatcher} for static resources in
|
||||
* commonly used locations.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class StaticResourceRequest {
|
||||
|
||||
private StaticResourceRequest() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher that includes all commonly used {@link Location Locations}. The
|
||||
* {@link StaticResourceRequestMatcher#excluding(Location, Location...) excluding} method
|
||||
* can be used to remove specific locations if required. For example:
|
||||
* <pre class="code">
|
||||
* StaticResourceRequest.toCommonLocations().excluding(Location.CSS)
|
||||
* </pre>
|
||||
* @return the configured {@link RequestMatcher}
|
||||
*/
|
||||
public static StaticResourceRequestMatcher toCommonLocations() {
|
||||
return to(EnumSet.allOf(Location.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher that includes the specified {@link Location Locations}. For
|
||||
* example: <pre class="code">
|
||||
* StaticResourceRequest.to(Location.CSS, Location.JAVA_SCRIPT)
|
||||
* </pre>
|
||||
* @param first the first location to include
|
||||
* @param rest additional locations to include
|
||||
* @return the configured {@link RequestMatcher}
|
||||
*/
|
||||
public static StaticResourceRequestMatcher to(Location first, Location... rest) {
|
||||
return to(EnumSet.of(first, rest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher that includes the specified {@link Location Locations}. For
|
||||
* example: <pre class="code">
|
||||
* StaticResourceRequest.to(locations)
|
||||
* </pre>
|
||||
* @param locations the locations to include
|
||||
* @return the configured {@link RequestMatcher}
|
||||
*/
|
||||
public static StaticResourceRequestMatcher to(Set<Location> locations) {
|
||||
Assert.notNull(locations, "Locations must not be null");
|
||||
return new StaticResourceRequestMatcher(new LinkedHashSet<>(locations));
|
||||
}
|
||||
|
||||
public enum Location {
|
||||
|
||||
/**
|
||||
* Resources under {@code "/css"}.
|
||||
*/
|
||||
CSS("/css/**"),
|
||||
|
||||
/**
|
||||
* Resources under {@code "/js"}.
|
||||
*/
|
||||
JAVA_SCRIPT("/js/**"),
|
||||
|
||||
/**
|
||||
* Resources under {@code "/images"}.
|
||||
*/
|
||||
IMAGES("/images/**"),
|
||||
|
||||
/**
|
||||
* Resources under {@code "/webjars"}.
|
||||
*/
|
||||
WEB_JARS("/webjars/**"),
|
||||
|
||||
/**
|
||||
* The {@code "favicon.ico"} resource.
|
||||
*/
|
||||
FAVICON("/**/favicon.ico");
|
||||
|
||||
private String[] patterns;
|
||||
|
||||
Location(String... patterns) {
|
||||
this.patterns = patterns;
|
||||
}
|
||||
|
||||
Stream<String> getPatterns() {
|
||||
return Arrays.stream(this.patterns);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The request matcher used to match against resource {@link Location Locations}.
|
||||
*/
|
||||
public final static class StaticResourceRequestMatcher
|
||||
extends ApplicationContextRequestMatcher<ServerProperties> {
|
||||
|
||||
private final Set<Location> locations;
|
||||
|
||||
private RequestMatcher delegate;
|
||||
|
||||
private StaticResourceRequestMatcher(Set<Location> locations) {
|
||||
super(ServerProperties.class);
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link StaticResourceRequestMatcher} based on this one but excluding the
|
||||
* specified locations.
|
||||
* @param first the first location to exclude
|
||||
* @param rest additional locations to exclude
|
||||
* @return a new {@link StaticResourceRequestMatcher}
|
||||
*/
|
||||
public StaticResourceRequestMatcher excluding(Location first, Location... rest) {
|
||||
return excluding(EnumSet.of(first, rest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link StaticResourceRequestMatcher} based on this one but excluding the
|
||||
* specified locations.
|
||||
* @param locations the locations to exclude
|
||||
* @return a new {@link StaticResourceRequestMatcher}
|
||||
*/
|
||||
public StaticResourceRequestMatcher excluding(Set<Location> locations) {
|
||||
Assert.notNull(locations, "Locations must not be null");
|
||||
Set<Location> subset = new LinkedHashSet<>(this.locations);
|
||||
subset.removeAll(locations);
|
||||
return new StaticResourceRequestMatcher(subset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialized(ServerProperties serverProperties) {
|
||||
this.delegate = new OrRequestMatcher(getDelegateMatchers(serverProperties));
|
||||
}
|
||||
|
||||
private List<RequestMatcher> getDelegateMatchers(
|
||||
ServerProperties serverProperties) {
|
||||
return getPatterns(serverProperties).map(AntPathRequestMatcher::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Stream<String> getPatterns(ServerProperties serverProperties) {
|
||||
return this.locations.stream().flatMap(Location::getPatterns)
|
||||
.map(serverProperties.getServlet()::getPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(HttpServletRequest request, ServerProperties context) {
|
||||
return this.delegate.matches(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.assertj.core.api.AssertDelegateTarget;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.autoconfigure.security.StaticResourceRequest.Location;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests for {@link StaticResourceRequest}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class StaticResourceRequestTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void toCommonLocationsShouldMatchCommonLocations() throws Exception {
|
||||
RequestMatcher matcher = StaticResourceRequest.toCommonLocations();
|
||||
assertMatcher(matcher).matches("/css/file.css");
|
||||
assertMatcher(matcher).matches("/js/file.js");
|
||||
assertMatcher(matcher).matches("/images/file.css");
|
||||
assertMatcher(matcher).matches("/webjars/file.css");
|
||||
assertMatcher(matcher).matches("/foo/favicon.ico");
|
||||
assertMatcher(matcher).doesNotMatch("/bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCommonLocationsWithExcludeShouldNotMatchExcluded() throws Exception {
|
||||
RequestMatcher matcher = StaticResourceRequest.toCommonLocations()
|
||||
.excluding(Location.CSS);
|
||||
assertMatcher(matcher).doesNotMatch("/css/file.css");
|
||||
assertMatcher(matcher).matches("/js/file.js");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toLocationShouldMatchLocation() throws Exception {
|
||||
RequestMatcher matcher = StaticResourceRequest.to(Location.CSS);
|
||||
assertMatcher(matcher).matches("/css/file.css");
|
||||
assertMatcher(matcher).doesNotMatch("/js/file.js");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toLocationWhenHasServletPathShouldMatchLocation() throws Exception {
|
||||
ServerProperties serverProperties = new ServerProperties();
|
||||
serverProperties.getServlet().setPath("/foo");
|
||||
RequestMatcher matcher = StaticResourceRequest.to(Location.CSS);
|
||||
assertMatcher(matcher, serverProperties).matches("/foo", "/css/file.css");
|
||||
assertMatcher(matcher, serverProperties).doesNotMatch("/foo", "/js/file.js");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toLocationsFromSetWhenSetIsNullShouldThrowException() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Locations must not be null");
|
||||
StaticResourceRequest.to((Set<Location>) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void excludeFromSetWhenSetIsNullShouldThrowException() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Locations must not be null");
|
||||
StaticResourceRequest.toCommonLocations().excluding((Set<Location>) null);
|
||||
}
|
||||
|
||||
private RequestMatcherAssert assertMatcher(RequestMatcher matcher) {
|
||||
StaticWebApplicationContext context = new StaticWebApplicationContext();
|
||||
context.registerBean(ServerProperties.class);
|
||||
return assertThat(new RequestMatcherAssert(context, matcher));
|
||||
}
|
||||
|
||||
private RequestMatcherAssert assertMatcher(RequestMatcher matcher,
|
||||
ServerProperties serverProperties) {
|
||||
StaticWebApplicationContext context = new StaticWebApplicationContext();
|
||||
context.registerBean(ServerProperties.class, () -> serverProperties);
|
||||
return assertThat(new RequestMatcherAssert(context, matcher));
|
||||
}
|
||||
|
||||
private static class RequestMatcherAssert implements AssertDelegateTarget {
|
||||
|
||||
private final WebApplicationContext context;
|
||||
|
||||
private final RequestMatcher matcher;
|
||||
|
||||
RequestMatcherAssert(WebApplicationContext context, RequestMatcher matcher) {
|
||||
this.context = context;
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
public void matches(String path) {
|
||||
matches(mockRequest(path));
|
||||
}
|
||||
|
||||
public void matches(String servletPath, String path) {
|
||||
matches(mockRequest(servletPath, path));
|
||||
}
|
||||
|
||||
private void matches(HttpServletRequest request) {
|
||||
assertThat(this.matcher.matches(request))
|
||||
.as("Matches " + getRequestPath(request)).isTrue();
|
||||
}
|
||||
|
||||
public void doesNotMatch(String path) {
|
||||
doesNotMatch(mockRequest(path));
|
||||
}
|
||||
|
||||
public void doesNotMatch(String servletPath, String path) {
|
||||
doesNotMatch(mockRequest(servletPath, path));
|
||||
}
|
||||
|
||||
private void doesNotMatch(HttpServletRequest request) {
|
||||
assertThat(this.matcher.matches(request))
|
||||
.as("Does not match " + getRequestPath(request)).isFalse();
|
||||
}
|
||||
|
||||
private MockHttpServletRequest mockRequest(String path) {
|
||||
return mockRequest(null, path);
|
||||
}
|
||||
|
||||
private MockHttpServletRequest mockRequest(String servletPath, String path) {
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
servletContext.setAttribute(
|
||||
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
|
||||
this.context);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(servletContext);
|
||||
if (servletPath != null) {
|
||||
request.setServletPath(servletPath);
|
||||
}
|
||||
request.setPathInfo(path);
|
||||
return request;
|
||||
}
|
||||
|
||||
private String getRequestPath(HttpServletRequest request) {
|
||||
String url = request.getServletPath();
|
||||
if (request.getPathInfo() != null) {
|
||||
url += request.getPathInfo();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package sample.actuator.customsecurity;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.security.EndpointRequest;
|
||||
import org.springframework.boot.autoconfigure.security.StaticResourceRequest;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
@ -17,18 +19,17 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// FIXME
|
||||
// @formatter:off
|
||||
// http.authorizeRequests()
|
||||
// .requestMatchers(endpointIds("status", "info")).permitAll()
|
||||
// .requestMatchers(endpointIds(SpringBootSecurity.ALL_ENDPOINTS)).hasRole("ACTUATOR")
|
||||
// .requestMatchers(staticResources()).permitAll()
|
||||
// .antMatchers("/foo").permitAll()
|
||||
// .antMatchers("/**").hasRole("USER")
|
||||
// .and()
|
||||
// .cors()
|
||||
// .and()
|
||||
// .httpBasic();
|
||||
http.authorizeRequests()
|
||||
.requestMatchers(EndpointRequest.to("status", "info")).permitAll()
|
||||
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
|
||||
.requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll()
|
||||
.antMatchers("/foo").permitAll()
|
||||
.antMatchers("/**").hasRole("USER")
|
||||
.and()
|
||||
.cors()
|
||||
.and()
|
||||
.httpBasic();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package sample.secure.oauth2.actuator;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.security.EndpointRequest;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
@ -16,12 +17,11 @@ public class ActuatorSecurityConfiguration extends WebSecurityConfigurerAdapter
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// FIXME
|
||||
// @formatter:off
|
||||
// http.requestMatcher(ALL_ENDPOINTS).authorizeRequests()
|
||||
// .antMatchers("/**").authenticated()
|
||||
// .and()
|
||||
// .httpBasic();
|
||||
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests()
|
||||
.antMatchers("/**").authenticated()
|
||||
.and()
|
||||
.httpBasic();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ package sample.security.method;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.security.EndpointRequest;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -104,9 +105,12 @@ public class SampleMethodSecurityApplication implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// FIXME
|
||||
// http.requestMatcher(ALL_ENDPOINTS)
|
||||
// .authorizeRequests().anyRequest().authenticated().and().httpBasic();
|
||||
// @formatter:off
|
||||
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.httpBasic();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.security.StaticResourceRequest;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
@ -62,15 +63,14 @@ public class SampleWebSecureApplication implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// FIXME
|
||||
// @formatter:off
|
||||
// http.authorizeRequests()
|
||||
// .requestMatchers(staticResources()).permitAll()
|
||||
// .anyRequest().fullyAuthenticated()
|
||||
// .and()
|
||||
// .formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
|
||||
// .and()
|
||||
// .logout().permitAll();
|
||||
http.authorizeRequests()
|
||||
.requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll()
|
||||
.anyRequest().fullyAuthenticated()
|
||||
.and()
|
||||
.formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
|
||||
.and()
|
||||
.logout().permitAll();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
@ -259,6 +259,11 @@
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
@ -372,4 +377,4 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.security;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
/**
|
||||
* {@link ApplicationContext} backed {@link RequestMatcher}. Can work directly with the
|
||||
* {@link ApplicationContext}, obtain an existing bean or
|
||||
* {@link AutowireCapableBeanFactory#createBean(Class, int, boolean) create a new bean}
|
||||
* that is autowired in the usual way.
|
||||
*
|
||||
* @param <C> The type of the context that the match method actually needs to use. Can be
|
||||
* an {@link ApplicationContext}, a class of an {@link ApplicationContext#getBean(Class)
|
||||
* existing bean} or a custom type that will be
|
||||
* {@link AutowireCapableBeanFactory#createBean(Class, int, boolean) created} on demand.
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public abstract class ApplicationContextRequestMatcher<C> implements RequestMatcher {
|
||||
|
||||
private final Class<? extends C> contextClass;
|
||||
|
||||
private volatile C context;
|
||||
|
||||
private Object contextLock = new Object();
|
||||
|
||||
public ApplicationContextRequestMatcher(Class<? extends C> contextClass) {
|
||||
Assert.notNull(contextClass, "Context class must not be null");
|
||||
this.contextClass = contextClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
return matches(request, getContext(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether the rule implemented by the strategy matches the supplied request.
|
||||
* @param request the source request
|
||||
* @param context the context instance
|
||||
* @return if the request matches
|
||||
*/
|
||||
protected abstract boolean matches(HttpServletRequest request, C context);
|
||||
|
||||
private C getContext(HttpServletRequest request) {
|
||||
if (this.context == null) {
|
||||
synchronized (this.contextLock) {
|
||||
this.context = createContext(request);
|
||||
initialized(this.context);
|
||||
}
|
||||
}
|
||||
return this.context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the context has been initialized.
|
||||
* @param context the initialized context
|
||||
*/
|
||||
protected void initialized(C context) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private C createContext(HttpServletRequest request) {
|
||||
WebApplicationContext context = WebApplicationContextUtils
|
||||
.getRequiredWebApplicationContext(request.getServletContext());
|
||||
if (this.contextClass.isInstance(context)) {
|
||||
return (C) context;
|
||||
}
|
||||
try {
|
||||
return context.getBean(this.contextClass);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return (C) context.getAutowireCapableBeanFactory().createBean(
|
||||
this.contextClass, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes and utilities for Spring Security.
|
||||
*/
|
||||
package org.springframework.boot.security;
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.security;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ApplicationContextRequestMatcher}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ApplicationContextRequestMatcherTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void createWhenContextClassIsNullShouldThrowException() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Context class must not be null");
|
||||
new TestApplicationContextRequestMatcher<>(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenContextClassIsApplicationContextShouldProvideContext()
|
||||
throws Exception {
|
||||
StaticWebApplicationContext context = createWebApplicationContext();
|
||||
assertThat(new TestApplicationContextRequestMatcher<>(ApplicationContext.class)
|
||||
.callMatchesAndReturnProvidedContext(context)).isEqualTo(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenContextClassIsExistingBeanShouldProvideBean()
|
||||
throws Exception {
|
||||
StaticWebApplicationContext context = createWebApplicationContext();
|
||||
context.registerSingleton("existingBean", ExistingBean.class);
|
||||
assertThat(new TestApplicationContextRequestMatcher<>(ExistingBean.class)
|
||||
.callMatchesAndReturnProvidedContext(context))
|
||||
.isEqualTo(context.getBean(ExistingBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenContextClassIsNewBeanShouldProvideBean() throws Exception {
|
||||
StaticWebApplicationContext context = createWebApplicationContext();
|
||||
context.registerSingleton("existingBean", ExistingBean.class);
|
||||
assertThat(new TestApplicationContextRequestMatcher<>(NewBean.class)
|
||||
.callMatchesAndReturnProvidedContext(context).getBean())
|
||||
.isEqualTo(context.getBean(ExistingBean.class));
|
||||
}
|
||||
|
||||
private StaticWebApplicationContext createWebApplicationContext() {
|
||||
StaticWebApplicationContext context = new StaticWebApplicationContext();
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
context.setServletContext(servletContext);
|
||||
servletContext.setAttribute(
|
||||
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
|
||||
return context;
|
||||
}
|
||||
|
||||
static class ExistingBean {
|
||||
|
||||
}
|
||||
|
||||
static class NewBean {
|
||||
|
||||
private final ExistingBean bean;
|
||||
|
||||
NewBean(ExistingBean bean) {
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
public ExistingBean getBean() {
|
||||
return this.bean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestApplicationContextRequestMatcher<C>
|
||||
extends ApplicationContextRequestMatcher<C> {
|
||||
|
||||
private C provdedContext;
|
||||
|
||||
TestApplicationContextRequestMatcher(Class<? extends C> context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public C callMatchesAndReturnProvidedContext(WebApplicationContext context) {
|
||||
return callMatchesAndReturnProvidedContext(
|
||||
new MockHttpServletRequest(context.getServletContext()));
|
||||
}
|
||||
|
||||
public C callMatchesAndReturnProvidedContext(HttpServletRequest request) {
|
||||
matches(request);
|
||||
return getProvdedContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(HttpServletRequest request, C context) {
|
||||
this.provdedContext = context;
|
||||
return false;
|
||||
}
|
||||
|
||||
public C getProvdedContext() {
|
||||
return this.provdedContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user