Ver código fonte

Lookup HandlerMappingIntrospector from Bean

Rob Winch 8 anos atrás
pai
commit
04d14fb6fe

+ 9 - 2
config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java

@@ -15,6 +15,7 @@
  */
  */
 package org.springframework.security.config.annotation.web;
 package org.springframework.security.config.annotation.web;
 
 
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 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;
@@ -42,6 +43,8 @@ import java.util.List;
  * @since 3.2
  * @since 3.2
  */
  */
 public abstract class AbstractRequestMatcherRegistry<C> {
 public abstract class AbstractRequestMatcherRegistry<C> {
+	private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
+
 	private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
 	private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
 
 
 	private ApplicationContext context;
 	private ApplicationContext context;
@@ -160,8 +163,12 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 			String... mvcPatterns) {
 			String... mvcPatterns) {
 		boolean isServlet30 = ClassUtils.isPresent("javax.servlet.ServletRegistration", getClass().getClassLoader());
 		boolean isServlet30 = ClassUtils.isPresent("javax.servlet.ServletRegistration", getClass().getClassLoader());
 		ObjectPostProcessor<Object> opp = this.context.getBean(ObjectPostProcessor.class);
 		ObjectPostProcessor<Object> opp = this.context.getBean(ObjectPostProcessor.class);
-		HandlerMappingIntrospector introspector = new HandlerMappingIntrospector(
-				this.context);
+		if(!this.context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
+			throw new NoSuchBeanDefinitionException("A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME +" of type " + HandlerMappingIntrospector.class.getName()
+				+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
+		}
+		HandlerMappingIntrospector introspector = this.context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
+			HandlerMappingIntrospector.class);
 		List<MvcRequestMatcher> matchers = new ArrayList<MvcRequestMatcher>(
 		List<MvcRequestMatcher> matchers = new ArrayList<MvcRequestMatcher>(
 				mvcPatterns.length);
 				mvcPatterns.length);
 		for (String mvcPattern : mvcPatterns) {
 		for (String mvcPattern : mvcPatterns) {

+ 9 - 3
config/src/main/java/org/springframework/security/config/annotation/web/configurers/CorsConfigurer.java

@@ -15,6 +15,7 @@
  */
  */
 package org.springframework.security.config.annotation.web.configurers;
 package org.springframework.security.config.annotation.web.configurers;
 
 
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContext;
 import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
 import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -98,7 +99,9 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>>
 		return null;
 		return null;
 	}
 	}
 
 
+
 	static class MvcCorsFilter {
 	static class MvcCorsFilter {
+		private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
 		/**
 		/**
 		 * This needs to be isolated into a separate class as Spring MVC is an optional
 		 * This needs to be isolated into a separate class as Spring MVC is an optional
 		 * dependency and will potentially cause ClassLoading issues
 		 * dependency and will potentially cause ClassLoading issues
@@ -106,9 +109,12 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>>
 		 * @return
 		 * @return
 		 */
 		 */
 		private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
 		private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
-			HandlerMappingIntrospector mappingIntrospector = new HandlerMappingIntrospector(
-					context);
+			if(!context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
+				throw new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME +" of type " + HandlerMappingIntrospector.class.getName()
+						+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
+			}
+			HandlerMappingIntrospector mappingIntrospector = context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector.class);
 			return new CorsFilter(mappingIntrospector);
 			return new CorsFilter(mappingIntrospector);
 		}
 		}
 	}
 	}
-}
+}

+ 3 - 6
config/src/main/java/org/springframework/security/config/http/CorsBeanDefinitionParser.java

@@ -15,17 +15,16 @@
  */
  */
 package org.springframework.security.config.http;
 package org.springframework.security.config.http;
 
 
-import org.w3c.dom.Element;
-
 import org.springframework.beans.BeanMetadataElement;
 import org.springframework.beans.BeanMetadataElement;
 import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.beans.factory.BeanCreationException;
-import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
 import org.springframework.beans.factory.config.RuntimeBeanReference;
 import org.springframework.beans.factory.config.RuntimeBeanReference;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.util.StringUtils;
 import org.springframework.web.filter.CorsFilter;
 import org.springframework.web.filter.CorsFilter;
+import org.w3c.dom.Element;
 
 
 /**
 /**
  * Parser for the {@code CorsFilter}.
  * Parser for the {@code CorsFilter}.
@@ -71,8 +70,6 @@ public class CorsBeanDefinitionParser {
 			return null;
 			return null;
 		}
 		}
 
 
-		BeanDefinitionBuilder configurationSourceBldr = BeanDefinitionBuilder.rootBeanDefinition(HANDLER_MAPPING_INTROSPECTOR);
-		configurationSourceBldr.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
-		return configurationSourceBldr.getBeanDefinition();
+		return new RootBeanDefinition(HandlerMappingIntrospectorFactoryBean.class);
 	}
 	}
 }
 }

+ 19 - 3
config/src/main/java/org/springframework/security/config/http/HandlerMappingIntrospectorFactoryBean.java

@@ -17,6 +17,8 @@
 package org.springframework.security.config.http;
 package org.springframework.security.config.http;
 
 
 import org.springframework.beans.BeansException;
 import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
 import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
@@ -28,12 +30,22 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
  * @author Rob Winch
  * @author Rob Winch
  * @since 4.1.1
  * @since 4.1.1
  */
  */
-class HandlerMappingIntrospectorFactoryBean implements ApplicationContextAware {
+class HandlerMappingIntrospectorFactoryBean implements FactoryBean<HandlerMappingIntrospector>, ApplicationContextAware {
+	private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
 
 
 	private ApplicationContext context;
 	private ApplicationContext context;
 
 
-	HandlerMappingIntrospector createHandlerMappingIntrospector() {
-		return new HandlerMappingIntrospector(this.context);
+	public HandlerMappingIntrospector getObject() {
+		if(!this.context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
+			throw new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME +" of type " + HandlerMappingIntrospector.class.getName()
+				+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
+		}
+		return this.context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector.class);
+	}
+
+	@Override
+	public Class<?> getObjectType() {
+		return HandlerMappingIntrospector.class;
 	}
 	}
 
 
 	/*
 	/*
@@ -48,4 +60,8 @@ class HandlerMappingIntrospectorFactoryBean implements ApplicationContextAware {
 		this.context = applicationContext;
 		this.context = applicationContext;
 	}
 	}
 
 
+	@Override
+	public boolean isSingleton() {
+		return true;
+	}
 }
 }

+ 2 - 14
config/src/main/java/org/springframework/security/config/http/MatcherType.java

@@ -37,8 +37,7 @@ public enum MatcherType {
 	ant(AntPathRequestMatcher.class), regex(RegexRequestMatcher.class), ciRegex(
 	ant(AntPathRequestMatcher.class), regex(RegexRequestMatcher.class), ciRegex(
 			RegexRequestMatcher.class), mvc(MvcRequestMatcher.class);
 			RegexRequestMatcher.class), mvc(MvcRequestMatcher.class);
 
 
-	private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
-	private static final String HANDLER_MAPPING_INTROSPECTOR_FACTORY_BEAN_NAME = "org.springframework.security.config.http.HandlerMappingIntrospectorFactoryBean";
+	private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
 
 
 	private static final String ATT_MATCHER_TYPE = "request-matcher";
 	private static final String ATT_MATCHER_TYPE = "request-matcher";
 
 
@@ -61,18 +60,7 @@ public enum MatcherType {
 				.rootBeanDefinition(type);
 				.rootBeanDefinition(type);
 
 
 		if (this == mvc) {
 		if (this == mvc) {
-			if (!pc.getRegistry().isBeanNameInUse(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
-				BeanDefinitionBuilder hmifb = BeanDefinitionBuilder
-						.rootBeanDefinition(HandlerMappingIntrospectorFactoryBean.class);
-				pc.getRegistry().registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_FACTORY_BEAN_NAME,
-						hmifb.getBeanDefinition());
-
-				RootBeanDefinition hmi = new RootBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME);
-				hmi.setFactoryBeanName(HANDLER_MAPPING_INTROSPECTOR_FACTORY_BEAN_NAME);
-				hmi.setFactoryMethodName("createHandlerMappingIntrospector");
-				pc.getRegistry().registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, hmi);
-			}
-			matcherBldr.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME);
+			matcherBldr.addConstructorArgValue(new RootBeanDefinition(HandlerMappingIntrospectorFactoryBean.class));
 		}
 		}
 
 
 		matcherBldr.addConstructorArgValue(path);
 		matcherBldr.addConstructorArgValue(path);

+ 7 - 11
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/CorsConfigurerTests.groovy

@@ -15,6 +15,9 @@
  */
  */
 package org.springframework.security.config.annotation.web.configurers
 package org.springframework.security.config.annotation.web.configurers
 
 
+import org.springframework.beans.factory.BeanCreationException
+import org.springframework.beans.factory.NoSuchBeanDefinitionException
+
 import javax.servlet.http.HttpServletResponse
 import javax.servlet.http.HttpServletResponse
 
 
 import org.springframework.context.annotation.Bean
 import org.springframework.context.annotation.Bean
@@ -36,19 +39,12 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc
  */
  */
 class CorsConfigurerTests extends BaseSpringSpec {
 class CorsConfigurerTests extends BaseSpringSpec {
 
 
-	def "HandlerMappingIntrospector default"() {
-		setup:
-		loadConfig(DefaultCorsConfig)
+	def "No MVC throws meaningful error"() {
 		when:
 		when:
-		addCors()
-		springSecurityFilterChain.doFilter(request,response,chain)
+		loadConfig(DefaultCorsConfig)
 		then:
 		then:
-		responseHeaders == ['X-Content-Type-Options':'nosniff',
-			'X-Frame-Options':'DENY',
-			'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
-			'Expires' : '0',
-			'Pragma':'no-cache',
-			'X-XSS-Protection' : '1; mode=block']
+		BeanCreationException success = thrown()
+		success.message.contains("Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext")
 	}
 	}
 
 
 	@EnableWebSecurity
 	@EnableWebSecurity

+ 6 - 11
config/src/test/groovy/org/springframework/security/config/http/HttpCorsConfigTests.groovy

@@ -12,6 +12,8 @@
  */
  */
 package org.springframework.security.config.http
 package org.springframework.security.config.http
 
 
+import org.springframework.beans.factory.BeanCreationException
+
 import javax.servlet.http.HttpServletResponse
 import javax.servlet.http.HttpServletResponse
 
 
 import org.springframework.http.*
 import org.springframework.http.*
@@ -38,24 +40,17 @@ class HttpCorsConfigTests extends AbstractHttpConfigTests {
 		chain = new MockFilterChain()
 		chain = new MockFilterChain()
 	}
 	}
 
 
-	def "HandlerMappingIntrospector default"() {
-		setup:
+	def "No MVC throws meaningful error"() {
+		when:
 		xml.http('entry-point-ref' : 'ep') {
 		xml.http('entry-point-ref' : 'ep') {
 			'cors'()
 			'cors'()
 			'intercept-url'(pattern:'/**', access: 'authenticated')
 			'intercept-url'(pattern:'/**', access: 'authenticated')
 		}
 		}
 		bean('ep', Http403ForbiddenEntryPoint)
 		bean('ep', Http403ForbiddenEntryPoint)
 		createAppContext()
 		createAppContext()
-		when:
-		addCors()
-		springSecurityFilterChain.doFilter(request,response,chain)
 		then:
 		then:
-		responseHeaders == ['X-Content-Type-Options':'nosniff',
-			'X-Frame-Options':'DENY',
-			'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
-			'Expires' : '0',
-			'Pragma':'no-cache',
-			'X-XSS-Protection' : '1; mode=block']
+		BeanCreationException success = thrown()
+		success.message.contains("Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext")
 	}
 	}
 
 
 	def "HandlerMappingIntrospector explicit"() {
 	def "HandlerMappingIntrospector explicit"() {

+ 1 - 0
config/src/test/groovy/org/springframework/security/config/http/InterceptUrlConfigTests.groovy

@@ -252,6 +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'")
 		}
 		}
+		xml.'mvc:annotation-driven'()
 		createWebAppContext(servletContext)
 		createWebAppContext(servletContext)
 		when: 'user can access'
 		when: 'user can access'
 		request.servletPath = '/user/user/abc'
 		request.servletPath = '/user/user/abc'

+ 58 - 0
docs/manual/src/docs/asciidoc/index.adoc

@@ -6733,6 +6733,64 @@ NOTE: Spring Security provides the configuration using Spring MVC's http://docs.
 Spring Security provides deep integration with how Spring MVC matches on URLs with `MvcRequestMatcher`.
 Spring Security provides deep integration with how Spring MVC matches on URLs with `MvcRequestMatcher`.
 This is helpful to ensure your Security rules match the logic used to handle your requests.
 This is helpful to ensure your Security rules match the logic used to handle your requests.
 
 
+In order to use `MvcRequestMatcher` you must place the Spring Security Configuration in the same `ApplicationContext` as your `DispatcherServlet`.
+This is necessary because Spring Security's `MvcRequestMatcher` expects a `HandlerMappingIntrospector` bean with the name of `mvcHandlerMappingIntrospector` to be registered by your Spring MVC configuration that is used to perform the matching.
+
+For a `web.xml` this means that you should place your configuration in the `DispatcherServlet.xml`.
+
+[source,xml]
+----
+<listener>
+  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+</listener>
+
+<!-- All Spring Configuration (both MVC and Security) are in /WEB-INF/spring/ -->
+<context-param>
+  <param-name>contextConfigLocation</param-name>
+  <param-value>/WEB-INF/spring/*.xml</param-value>
+</context-param>
+
+<servlet>
+  <servlet-name>spring</servlet-name>
+  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+  <!-- Load from the ContextLoaderListener -->
+  <init-param>
+    <param-name>contextConfigLocation</param-name>
+    <param-value></param-value>
+  </init-param>
+</servlet>
+
+<servlet-mapping>
+  <servlet-name>spring</servlet-name>
+  <url-pattern>/</url-pattern>
+</servlet-mapping>
+----
+
+Below `WebSecurityConfiguration` in placed in the ``DispatcherServlet``s `ApplicationContext`.
+
+[source,java]
+----
+public class SecurityInitializer extends
+    AbstractAnnotationConfigDispatcherServletInitializer {
+
+  @Override
+  protected Class<?>[] getRootConfigClasses() {
+    return null;
+  }
+
+  @Override
+  protected Class<?>[] getServletConfigClasses() {
+    return new Class[] { RootConfiguration.class,
+        WebMvcConfiguration.class };
+  }
+
+  @Override
+  protected String[] getServletMappings() {
+    return new String[] { "/" };
+  }
+}
+----
+
 [NOTE]
 [NOTE]
 ====
 ====
 It is always recommended to provide authorization rules by matching on the `HttpServletRequest` and method security.
 It is always recommended to provide authorization rules by matching on the `HttpServletRequest` and method security.