mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-08 13:12:12 +00:00
MvcRequestMatcher servletPath Polish / XML Config
Fixes gh-4014
This commit is contained in:
parent
8a6d0cd16d
commit
dabcc5416a
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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'))))
|
||||||
|
@ -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) {
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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 });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user