|
@@ -18,7 +18,19 @@ package org.springframework.security.config.annotation.web.configuration;
|
|
|
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
|
|
|
+import jakarta.servlet.Filter;
|
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
|
+
|
|
|
|
+import org.springframework.beans.BeanMetadataElement;
|
|
import org.springframework.beans.BeansException;
|
|
import org.springframework.beans.BeansException;
|
|
|
|
+import org.springframework.beans.factory.FactoryBean;
|
|
|
|
+import org.springframework.beans.factory.config.BeanDefinition;
|
|
|
|
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
|
|
|
+import org.springframework.beans.factory.config.RuntimeBeanReference;
|
|
|
|
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
|
|
|
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|
|
|
+import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
|
|
|
+import org.springframework.beans.factory.support.ManagedList;
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.ApplicationContextAware;
|
|
import org.springframework.context.ApplicationContextAware;
|
|
import org.springframework.context.annotation.Bean;
|
|
import org.springframework.context.annotation.Bean;
|
|
@@ -26,13 +38,19 @@ import org.springframework.context.expression.BeanFactoryResolver;
|
|
import org.springframework.expression.BeanResolver;
|
|
import org.springframework.expression.BeanResolver;
|
|
import org.springframework.security.core.context.SecurityContextHolder;
|
|
import org.springframework.security.core.context.SecurityContextHolder;
|
|
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
|
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
|
|
|
+import org.springframework.security.web.FilterChainProxy;
|
|
|
|
+import org.springframework.security.web.SecurityFilterChain;
|
|
|
|
+import org.springframework.security.web.access.HandlerMappingIntrospectorRequestTransformer;
|
|
|
|
+import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
|
|
import org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver;
|
|
import org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver;
|
|
import org.springframework.security.web.method.annotation.CsrfTokenArgumentResolver;
|
|
import org.springframework.security.web.method.annotation.CsrfTokenArgumentResolver;
|
|
import org.springframework.security.web.method.annotation.CurrentSecurityContextArgumentResolver;
|
|
import org.springframework.security.web.method.annotation.CurrentSecurityContextArgumentResolver;
|
|
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
|
|
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
|
|
|
|
+import org.springframework.web.filter.CompositeFilter;
|
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
|
|
+import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
|
import org.springframework.web.servlet.support.RequestDataValueProcessor;
|
|
import org.springframework.web.servlet.support.RequestDataValueProcessor;
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -50,6 +68,8 @@ import org.springframework.web.servlet.support.RequestDataValueProcessor;
|
|
*/
|
|
*/
|
|
class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContextAware {
|
|
class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContextAware {
|
|
|
|
|
|
|
|
+ private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
|
|
|
|
+
|
|
private BeanResolver beanResolver;
|
|
private BeanResolver beanResolver;
|
|
|
|
|
|
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
|
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
|
@@ -84,4 +104,146 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Used to ensure Spring MVC request matching is cached.
|
|
|
|
+ *
|
|
|
|
+ * Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
|
|
|
|
+ * HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the
|
|
|
|
+ * AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name
|
|
|
|
+ * and then adds a {@link CompositeFilter} that contains
|
|
|
|
+ * {@link HandlerMappingIntrospector#createCacheFilter()} and the original
|
|
|
|
+ * FilterChainProxy under the original Bean name.
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ @Bean
|
|
|
|
+ static BeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() {
|
|
|
|
+ return new BeanDefinitionRegistryPostProcessor() {
|
|
|
|
+ @Override
|
|
|
|
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
|
|
|
+ if (!registry.containsBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
|
|
|
|
+ .rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
|
|
|
|
+ .addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
|
|
|
|
+ .getBeanDefinition();
|
|
|
|
+ registry.registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer",
|
|
|
|
+ hmiRequestTransformer);
|
|
|
|
+
|
|
|
|
+ String filterChainProxyBeanName = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME
|
|
|
|
+ + "Proxy";
|
|
|
|
+ BeanDefinition filterChainProxy = registry
|
|
|
|
+ .getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
|
|
|
|
+ registry.registerBeanDefinition(filterChainProxyBeanName, filterChainProxy);
|
|
|
|
+
|
|
|
|
+ BeanDefinitionBuilder hmiCacheFilterBldr = BeanDefinitionBuilder
|
|
|
|
+ .rootBeanDefinition(HandlerMappingIntrospectorCachFilterFactoryBean.class)
|
|
|
|
+ .setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
|
|
|
+
|
|
|
|
+ ManagedList<BeanMetadataElement> filters = new ManagedList<>();
|
|
|
|
+ filters.add(hmiCacheFilterBldr.getBeanDefinition());
|
|
|
|
+ filters.add(new RuntimeBeanReference(filterChainProxyBeanName));
|
|
|
|
+ BeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder
|
|
|
|
+ .rootBeanDefinition(SpringSecurityFilterCompositeFilter.class)
|
|
|
|
+ .addConstructorArgValue(filters);
|
|
|
|
+
|
|
|
|
+ registry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
|
|
|
|
+ registry.registerBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME,
|
|
|
|
+ compositeSpringSecurityFilterChainBldr.getBeanDefinition());
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * {@link FactoryBean} to defer creation of
|
|
|
|
+ * {@link HandlerMappingIntrospector#createCacheFilter()}
|
|
|
|
+ */
|
|
|
|
+ static class HandlerMappingIntrospectorCachFilterFactoryBean
|
|
|
|
+ implements ApplicationContextAware, FactoryBean<Filter> {
|
|
|
|
+
|
|
|
|
+ private ApplicationContext applicationContext;
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setApplicationContext(ApplicationContext applicationContext) {
|
|
|
|
+ this.applicationContext = applicationContext;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Filter getObject() throws Exception {
|
|
|
|
+ HandlerMappingIntrospector handlerMappingIntrospector = this.applicationContext
|
|
|
|
+ .getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector.class);
|
|
|
|
+ return handlerMappingIntrospector.createCacheFilter();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Class<?> getObjectType() {
|
|
|
|
+ return Filter.class;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Extension to {@link CompositeFilter} to expose private methods used by Spring
|
|
|
|
+ * Security's test support
|
|
|
|
+ */
|
|
|
|
+ static class SpringSecurityFilterCompositeFilter extends CompositeFilter {
|
|
|
|
+
|
|
|
|
+ private FilterChainProxy springSecurityFilterChain;
|
|
|
|
+
|
|
|
|
+ SpringSecurityFilterCompositeFilter(List<? extends Filter> filters) {
|
|
|
|
+ setFilters(filters); // for the parent
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setFilters(List<? extends Filter> filters) {
|
|
|
|
+ super.setFilters(filters);
|
|
|
|
+ this.springSecurityFilterChain = findFilterChainProxy(filters);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Used through reflection by Spring Security's Test support to lookup the
|
|
|
|
+ * FilterChainProxy Filters for a specific HttpServletRequest.
|
|
|
|
+ * @param request
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ private List<? extends Filter> getFilters(HttpServletRequest request) {
|
|
|
|
+ List<SecurityFilterChain> filterChains = getFilterChainProxy().getFilterChains();
|
|
|
|
+ for (SecurityFilterChain chain : filterChains) {
|
|
|
|
+ if (chain.matches(request)) {
|
|
|
|
+ return chain.getFilters();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Used by Spring Security's Test support to find the FilterChainProxy
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ private FilterChainProxy getFilterChainProxy() {
|
|
|
|
+ return this.springSecurityFilterChain;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Find the FilterChainProxy in a List of Filter
|
|
|
|
+ * @param filters
|
|
|
|
+ * @return non-null FilterChainProxy
|
|
|
|
+ * @throws IllegalStateException if the FilterChainProxy cannot be found
|
|
|
|
+ */
|
|
|
|
+ private static FilterChainProxy findFilterChainProxy(List<? extends Filter> filters) {
|
|
|
|
+ for (Filter filter : filters) {
|
|
|
|
+ if (filter instanceof FilterChainProxy fcp) {
|
|
|
|
+ return fcp;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ throw new IllegalStateException("Couldn't find FilterChainProxy in " + filters);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
}
|
|
}
|