MvcRequestMatcher servletPath Polish / XML Config

Fixes gh-4014
This commit is contained in:
Joe Grandja 2016-07-22 10:24:12 -04:00 committed by Rob Winch
parent 8a6d0cd16d
commit dabcc5416a
10 changed files with 229 additions and 104 deletions

View File

@ -15,15 +15,6 @@
*/ */
package org.springframework.security.config.annotation.web; package org.springframework.security.config.annotation.web;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.ObjectPostProcessor;
@ -34,9 +25,12 @@ import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher; import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector; import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** /**
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for * A base class for registering {@link RequestMatcher}'s. For example, it might allow for
* specifying which {@link RequestMatcher} require a certain level of authorization. * specifying which {@link RequestMatcher} require a certain level of authorization.
@ -171,12 +165,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
List<MvcRequestMatcher> matchers = new ArrayList<MvcRequestMatcher>( List<MvcRequestMatcher> matchers = new ArrayList<MvcRequestMatcher>(
mvcPatterns.length); mvcPatterns.length);
for (String mvcPattern : mvcPatterns) { for (String mvcPattern : mvcPatterns) {
MvcRequestMatcher matcher; MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, mvcPattern);
if(isServlet30) { if (isServlet30) {
matcher = new ServletPathValidatingtMvcRequestMatcher(introspector, mvcPattern);
opp.postProcess(matcher); opp.postProcess(matcher);
} else {
matcher = new MvcRequestMatcher(introspector, mvcPattern);
} }
if (method != null) { if (method != null) {
matcher.setMethod(method); matcher.setMethod(method);
@ -316,48 +307,4 @@ public abstract class AbstractRequestMatcherRegistry<C> {
} }
} }
static class ServletPathValidatingtMvcRequestMatcher extends MvcRequestMatcher implements SmartInitializingSingleton, ServletContextAware {
private ServletContext servletContext;
/**
* @param introspector
* @param pattern
*/
public ServletPathValidatingtMvcRequestMatcher(HandlerMappingIntrospector introspector,
String pattern) {
super(introspector, pattern);
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.SmartInitializingSingleton#afterSingletonsInstantiated()
*/
@Override
public void afterSingletonsInstantiated() {
if(getServletPath() != null) {
return;
}
Collection<? extends ServletRegistration> registrations = servletContext.getServletRegistrations().values();
for(ServletRegistration registration : registrations) {
Collection<String> mappings = registration.getMappings();
for(String mapping : mappings) {
if(mapping.startsWith("/") && mapping.length() > 1) {
throw new IllegalStateException(
"servletPath must not be null for mvcPattern \"" + getMvcPattern()
+ "\" when providing a servlet mapping of "
+ mapping + " for servlet "
+ registration.getClassName());
}
}
}
}
/* (non-Javadoc)
* @see org.springframework.web.context.ServletContextAware#setServletContext(javax.servlet.ServletContext)
*/
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}
} }

View File

@ -1351,7 +1351,7 @@
'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are
2 or more HttpServlet's registered in the ServletContext that have mappings starting with 2 or more HttpServlet's registered in the ServletContext that have mappings starting with
'/' and are different; 2) The pattern starts with the same value of a registered '/' and are different; 2) The pattern starts with the same value of a registered
HttpServlet path, excluding the default (root) HttpServlet '/' HttpServlet path, excluding the default (root) HttpServlet '/'.
</xs:documentation> </xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>

View File

@ -1,26 +1,25 @@
package org.springframework.security.config package org.springframework.security.config
import groovy.xml.MarkupBuilder import groovy.xml.MarkupBuilder
import org.mockito.Mockito
import org.mockito.Mockito; import org.springframework.context.ApplicationListener
import org.springframework.context.support.AbstractXmlApplicationContext import org.springframework.context.support.AbstractRefreshableApplicationContext
import org.springframework.mock.web.MockServletContext
import org.springframework.security.CollectingAppListener
import org.springframework.security.config.util.InMemoryXmlApplicationContext import org.springframework.security.config.util.InMemoryXmlApplicationContext
import org.springframework.security.config.util.InMemoryXmlWebApplicationContext
import org.springframework.security.core.context.SecurityContextHolder import org.springframework.security.core.context.SecurityContextHolder
import spock.lang.Specification import spock.lang.Specification
import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_XML
import org.springframework.context.ApplicationListener
import org.springframework.context.ApplicationEvent
import org.springframework.security.authentication.event.AbstractAuthenticationEvent
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent
import org.springframework.security.access.event.AbstractAuthorizationEvent
import org.springframework.security.CollectingAppListener
import javax.servlet.ServletContext
import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_XML
/** /**
* *
* @author Luke Taylor * @author Luke Taylor
*/ */
abstract class AbstractXmlConfigTests extends Specification { abstract class AbstractXmlConfigTests extends Specification {
AbstractXmlApplicationContext appContext; AbstractRefreshableApplicationContext appContext;
Writer writer; Writer writer;
MarkupBuilder xml; MarkupBuilder xml;
ApplicationListener appListener; ApplicationListener appListener;
@ -81,4 +80,27 @@ abstract class AbstractXmlConfigTests extends Specification {
appContext = new InMemoryXmlApplicationContext(writer.toString() + extraXml); appContext = new InMemoryXmlApplicationContext(writer.toString() + extraXml);
appContext.addApplicationListener(appListener); appContext.addApplicationListener(appListener);
} }
def createWebAppContext() {
createWebAppContext(AUTH_PROVIDER_XML);
}
def createWebAppContext(ServletContext servletContext) {
createWebAppContext(AUTH_PROVIDER_XML, servletContext);
}
def createWebAppContext(String extraXml) {
createWebAppContext(extraXml, null);
}
def createWebAppContext(String extraXml, ServletContext servletContext) {
appContext = new InMemoryXmlWebApplicationContext(writer.toString() + extraXml);
appContext.addApplicationListener(appListener);
if (servletContext != null) {
appContext.setServletContext(servletContext);
} else {
appContext.setServletContext(new MockServletContext());
}
appContext.refresh();
}
} }

View File

@ -16,7 +16,6 @@
package org.springframework.security.config.doc package org.springframework.security.config.doc
import groovy.util.slurpersupport.GPathResult; import groovy.util.slurpersupport.GPathResult;
import groovy.util.slurpersupport.NodeChild
import org.springframework.security.config.http.SecurityFilters import org.springframework.security.config.http.SecurityFilters
@ -89,7 +88,7 @@ class XsdDocumentedTests extends Specification {
def 'the latest schema is being validated'() { def 'the latest schema is being validated'() {
when: 'all the schemas are found' when: 'all the schemas are found'
def schemas = schemaDocument.getParentFile().list().findAll { it.endsWith('.xsd') } def schemas = schemaDocument.getParentFile().list().findAll { it.endsWith('.xsd') }
then: 'the count is equal to 8, if not then schemaDocument needs updated' then: 'the count is equal to 10, if not then schemaDocument needs updated'
schemas.size() == 10 schemas.size() == 10
} }

View File

@ -15,17 +15,25 @@
*/ */
package org.springframework.security.config.http package org.springframework.security.config.http
import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.Answer
import org.springframework.beans.factory.BeanCreationException
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
import org.springframework.mock.web.MockFilterChain import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.mock.web.MockServletContext
import org.springframework.security.access.SecurityConfig import org.springframework.security.access.SecurityConfig
import org.springframework.security.crypto.codec.Base64 import org.springframework.security.crypto.codec.Base64
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import javax.servlet.ServletContext
import javax.servlet.ServletRegistration
import javax.servlet.http.HttpServletResponse import javax.servlet.http.HttpServletResponse
import static org.mockito.Mockito.*
/** /**
* *
* @author Rob Winch * @author Rob Winch
@ -199,6 +207,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
def "intercept-url supports mvc matchers"() { def "intercept-url supports mvc matchers"() {
setup: setup:
MockServletContext servletContext = mockServletContext();
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET') MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
MockFilterChain chain = new MockFilterChain() MockFilterChain chain = new MockFilterChain()
@ -209,7 +218,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
bean('pathController',PathController) bean('pathController',PathController)
xml.'mvc:annotation-driven'() xml.'mvc:annotation-driven'()
createAppContext() createWebAppContext(servletContext)
when: when:
request.servletPath = "/path" request.servletPath = "/path"
springSecurityFilterChain.doFilter(request, response, chain) springSecurityFilterChain.doFilter(request, response, chain)
@ -235,6 +244,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
def "intercept-url mvc supports path variables"() { def "intercept-url mvc supports path variables"() {
setup: setup:
MockServletContext servletContext = mockServletContext();
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET') MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
MockFilterChain chain = new MockFilterChain() MockFilterChain chain = new MockFilterChain()
@ -242,7 +252,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
'http-basic'() 'http-basic'()
'intercept-url'(pattern: '/user/{un}/**', access: "#un == 'user'") 'intercept-url'(pattern: '/user/{un}/**', access: "#un == 'user'")
} }
createAppContext() createWebAppContext(servletContext)
when: 'user can access' when: 'user can access'
request.servletPath = '/user/user/abc' request.servletPath = '/user/user/abc'
springSecurityFilterChain.doFilter(request,response,chain) springSecurityFilterChain.doFilter(request,response,chain)
@ -266,6 +276,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
def "intercept-url mvc matchers with servlet path"() { def "intercept-url mvc matchers with servlet path"() {
setup: setup:
MockServletContext servletContext = mockServletContext("/spring");
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET') MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
MockHttpServletResponse response = new MockHttpServletResponse() MockHttpServletResponse response = new MockHttpServletResponse()
MockFilterChain chain = new MockFilterChain() MockFilterChain chain = new MockFilterChain()
@ -275,7 +286,7 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
} }
bean('pathController',PathController) bean('pathController',PathController)
xml.'mvc:annotation-driven'() xml.'mvc:annotation-driven'()
createAppContext() createWebAppContext(servletContext)
when: when:
request.servletPath = "/spring" request.servletPath = "/spring"
request.requestURI = "/spring/path" request.requestURI = "/spring/path"
@ -302,6 +313,30 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
response.status == HttpServletResponse.SC_UNAUTHORIZED response.status == HttpServletResponse.SC_UNAUTHORIZED
} }
def "intercept-url mvc matchers servlet path required"() {
when:
MockServletContext servletContext = mockServletContext("/spring");
xml.http('request-matcher':'mvc') {
'http-basic'()
'intercept-url'(pattern: '/path', access: "denyAll")
}
createWebAppContext(servletContext)
then:
thrown(BeanCreationException)
}
def "intercept-url mvc matchers servlet path NOT required"() {
when:
MockServletContext servletContext = mockServletContext();
xml.http('request-matcher':'mvc') {
'http-basic'()
'intercept-url'(pattern: '/path', access: "denyAll")
}
createWebAppContext(servletContext)
then:
noExceptionThrown()
}
def "intercept-url ant matcher with servlet path fails"() { def "intercept-url ant matcher with servlet path fails"() {
when: when:
xml.http('request-matcher':'ant') { xml.http('request-matcher':'ant') {
@ -352,6 +387,24 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
} }
} }
private ServletContext mockServletContext() {
return mockServletContext("/");
}
private ServletContext mockServletContext(String servletPath) {
MockServletContext servletContext = spy(new MockServletContext());
final ServletRegistration registration = mock(ServletRegistration.class);
when(registration.getMappings()).thenReturn(Collections.singleton(servletPath));
Answer<Map<String, ? extends ServletRegistration>> answer = new Answer<Map<String, ? extends ServletRegistration>>() {
@Override
public Map<String, ? extends ServletRegistration> answer(InvocationOnMock invocation) throws Throwable {
return Collections.<String, ServletRegistration>singletonMap("spring", registration);
}
};
when(servletContext.getServletRegistrations()).thenAnswer(answer);
return servletContext;
}
def login(MockHttpServletRequest request, String username, String password) { def login(MockHttpServletRequest request, String username, String password) {
String toEncode = username + ':' + password String toEncode = username + ':' + password
request.addHeader('Authorization','Basic ' + new String(Base64.encode(toEncode.getBytes('UTF-8')))) request.addHeader('Authorization','Basic ' + new String(Base64.encode(toEncode.getBytes('UTF-8'))))

View File

@ -16,14 +16,6 @@
package org.springframework.security.config.annotation.web; package org.springframework.security.config.annotation.web;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import javax.servlet.Registration;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -31,14 +23,17 @@ import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import org.springframework.mock.web.MockServletContext; import org.springframework.mock.web.MockServletContext;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.ServletPathValidatingtMvcRequestMatcher; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector; import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import static org.mockito.Mockito.mock; import javax.servlet.ServletContext;
import static org.mockito.Mockito.spy; import javax.servlet.ServletRegistration;
import static org.mockito.Mockito.when; import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import static org.mockito.Mockito.*;
/** /**
* @author Rob Winch * @author Rob Winch
@ -49,34 +44,34 @@ public class AbstractRequestMatcherRegistryTests {
@Mock @Mock
HandlerMappingIntrospector introspector; HandlerMappingIntrospector introspector;
ServletPathValidatingtMvcRequestMatcher matcher; MvcRequestMatcher matcher;
ServletContext servletContext; ServletContext servletContext;
@Before @Before
public void setup() { public void setup() {
servletContext = spy(new MockServletContext()); servletContext = spy(new MockServletContext());
matcher = new ServletPathValidatingtMvcRequestMatcher(introspector, "/foo"); matcher = new MvcRequestMatcher(introspector, "/foo");
matcher.setServletContext(servletContext); matcher.setServletContext(servletContext);
} }
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void servletPathValidatingtMvcRequestMatcherAfterSingletonsIntantiatedFailsWithSpringServlet() { public void servletPathValidatingMvcRequestMatcherAfterPropertiesSetFailsWithSpringServlet() throws Exception {
setMappings("/spring"); setMappings("/spring");
matcher.afterSingletonsInstantiated(); matcher.afterPropertiesSet();
} }
@Test @Test
public void servletPathValidatingtMvcRequestMatcherAfterSingletonsIntantiatedWithSpringServlet() { public void servletPathValidatingMvcRequestMatcherAfterPropertiesSetWithSpringServlet() throws Exception {
matcher.setServletPath("/spring"); matcher.setServletPath("/spring");
setMappings("/spring"); setMappings("/spring");
matcher.afterSingletonsInstantiated(); matcher.afterPropertiesSet();
} }
@Test @Test
public void servletPathValidatingtMvcRequestMatcherAfterSingletonsIntantiatedDefaultServlet() { public void servletPathValidatingMvcRequestMatcherAfterPropertiesSetDefaultServlet() throws Exception {
setMappings("/"); setMappings("/");
matcher.afterSingletonsInstantiated(); matcher.afterPropertiesSet();
} }
private void setMappings(String... mappings) { private void setMappings(String... mappings) {

View File

@ -27,6 +27,7 @@ import org.junit.Test;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -449,7 +450,7 @@ public class AuthorizeRequestsTests {
} }
} }
@Test(expected = IllegalStateException.class) @Test(expected = BeanCreationException.class)
public void mvcMatcherServletPathRequired() throws Exception { public void mvcMatcherServletPathRequired() throws Exception {
final ServletRegistration registration = mock(ServletRegistration.class); final ServletRegistration registration = mock(ServletRegistration.class);
when(registration.getMappings()).thenReturn(Collections.singleton("/spring")); when(registration.getMappings()).thenReturn(Collections.singleton("/spring"));
@ -502,4 +503,4 @@ public class AuthorizeRequestsTests {
this.context.getAutowireCapableBeanFactory().autowireBean(this); this.context.getAutowireCapableBeanFactory().autowireBean(this);
} }
} }

View File

@ -25,7 +25,7 @@ import org.springframework.security.util.InMemoryResource;
* @author Luke Taylor * @author Luke Taylor
*/ */
public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext { public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext {
private static final String BEANS_OPENING = "<b:beans xmlns='http://www.springframework.org/schema/security'\n" static final String BEANS_OPENING = "<b:beans xmlns='http://www.springframework.org/schema/security'\n"
+ " xmlns:context='http://www.springframework.org/schema/context'\n" + " xmlns:context='http://www.springframework.org/schema/context'\n"
+ " xmlns:b='http://www.springframework.org/schema/beans'\n" + " xmlns:b='http://www.springframework.org/schema/beans'\n"
+ " xmlns:aop='http://www.springframework.org/schema/aop'\n" + " xmlns:aop='http://www.springframework.org/schema/aop'\n"
@ -38,16 +38,18 @@ public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext
+ "http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd\n" + "http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd\n"
+ "http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\n" + "http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\n"
+ "http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-"; + "http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-";
private static final String BEANS_CLOSE = "</b:beans>\n"; static final String BEANS_CLOSE = "</b:beans>\n";
static final String SPRING_SECURITY_VERSION = "4.1";
Resource inMemoryXml; Resource inMemoryXml;
public InMemoryXmlApplicationContext(String xml) { public InMemoryXmlApplicationContext(String xml) {
this(xml, "4.1", null); this(xml, SPRING_SECURITY_VERSION, null);
} }
public InMemoryXmlApplicationContext(String xml, ApplicationContext parent) { public InMemoryXmlApplicationContext(String xml, ApplicationContext parent) {
this(xml, "4.1", parent); this(xml, SPRING_SECURITY_VERSION, parent);
} }
public InMemoryXmlApplicationContext(String xml, String secVersion, public InMemoryXmlApplicationContext(String xml, String secVersion,

View File

@ -0,0 +1,60 @@
/*
* Copyright 2012-2016 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.security.config.util;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.security.util.InMemoryResource;
import org.springframework.web.context.support.AbstractRefreshableWebApplicationContext;
import java.io.IOException;
import static org.springframework.security.config.util.InMemoryXmlApplicationContext.BEANS_CLOSE;
import static org.springframework.security.config.util.InMemoryXmlApplicationContext.BEANS_OPENING;
import static org.springframework.security.config.util.InMemoryXmlApplicationContext.SPRING_SECURITY_VERSION;
/**
* @author Joe Grandja
*/
public class InMemoryXmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
private Resource inMemoryXml;
public InMemoryXmlWebApplicationContext(String xml) {
this(xml, SPRING_SECURITY_VERSION, null);
}
public InMemoryXmlWebApplicationContext(String xml, ApplicationContext parent) {
this(xml, SPRING_SECURITY_VERSION, parent);
}
public InMemoryXmlWebApplicationContext(String xml, String secVersion,
ApplicationContext parent) {
String fullXml = BEANS_OPENING + secVersion + ".xsd'>\n" + xml + BEANS_CLOSE;
inMemoryXml = new InMemoryResource(fullXml);
setAllowBeanDefinitionOverriding(true);
setParent(parent);
}
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new Resource[] { inMemoryXml });
}
}

View File

@ -16,17 +16,24 @@
package org.springframework.security.web.servlet.util.matcher; package org.springframework.security.web.servlet.util.matcher;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestVariablesExtractor; import org.springframework.security.web.util.matcher.RequestVariablesExtractor;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import org.springframework.util.ClassUtils;
import org.springframework.util.PathMatcher; import org.springframework.util.PathMatcher;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector; import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import org.springframework.web.servlet.handler.MatchableHandlerMapping; import org.springframework.web.servlet.handler.MatchableHandlerMapping;
import org.springframework.web.servlet.handler.RequestMatchResult; import org.springframework.web.servlet.handler.RequestMatchResult;
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
@ -45,13 +52,18 @@ import java.util.Map;
* @since 4.1.1 * @since 4.1.1
*/ */
public class MvcRequestMatcher public class MvcRequestMatcher
implements RequestMatcher, RequestVariablesExtractor { implements RequestMatcher, RequestVariablesExtractor, InitializingBean, ServletContextAware {
private static final boolean isServlet30 = ClassUtils.isPresent(
"javax.servlet.ServletRegistration", MvcRequestMatcher.class.getClassLoader());
private final DefaultMatcher defaultMatcher = new DefaultMatcher(); private final DefaultMatcher defaultMatcher = new DefaultMatcher();
private final HandlerMappingIntrospector introspector; private final HandlerMappingIntrospector introspector;
private final String pattern; private final String pattern;
private HttpMethod method; private HttpMethod method;
private String servletPath; private String servletPath;
private ServletContext servletContext;
public MvcRequestMatcher(HandlerMappingIntrospector introspector, String pattern) { public MvcRequestMatcher(HandlerMappingIntrospector introspector, String pattern) {
this.introspector = introspector; this.introspector = introspector;
@ -100,6 +112,40 @@ public class MvcRequestMatcher
: result.extractUriTemplateVariables(); : result.extractUriTemplateVariables();
} }
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
// servletPath is required when at least one registered Servlet
// is mapped to a path other than the default (root) path '/'
if (this.servletContext == null || !isServlet30) {
return;
}
if (this.getServletPath() != null) {
return;
}
for (ServletRegistration registration : this.servletContext.getServletRegistrations().values()) {
for (String mapping : registration.getMappings()) {
if (mapping.startsWith("/") && mapping.length() > 1) {
throw new IllegalStateException(
"servletPath must not be null for mvcPattern \"" + this.getMvcPattern()
+ "\" when providing a servlet mapping of "
+ mapping + " for servlet "
+ registration.getClassName());
}
}
}
}
/* (non-Javadoc)
* @see org.springframework.web.context.ServletContextAware#setServletContext(javax.servlet.ServletContext)
*/
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
/** /**
* @param method the method to set * @param method the method to set
*/ */