代码之家  ›  专栏  ›  技术社区  ›  lainatnavi

是否可以将Spring的HttpFirewall配置为仅在某些URL-s上调用?

  •  0
  • lainatnavi  · 技术社区  · 5 年前

    在一个混合的web应用程序中,只有URI-s在下面 /springpath/** /春径/** HttpSecurity 配置,我可以用 antMatcher 排除其他资源 ,这个很好用。
    我面临的问题是 StrictHttpFirewall 正在为所有请求调用,包括不匹配的请求
    是否可以从spring的防火墙检查中排除URL-s?

    0 回复  |  直到 5 年前
        1
  •  1
  •   Rob Winch    5 年前

    如果要将Spring Security的请求限制在 /springpath/* 你可以绘制地图 Spring Security's FilterChainProxy .

    弹簧靴

    在Spring-Boot应用程序中,Spring-Boot的 SecurityFilterConfiguration automatically registers Spring Security's FilterChainProxy 与每个网址。您可以通过几个步骤来覆盖它:

    SecurityFilterAutoConfiguration 包括以下内容:

    @SpringBootApplication(exclude = SecurityFilterAutoConfiguration.class)
    

    接下来,你必须提供你自己的 FilterRegistrationBean . 这是一份稍微修改过的 安全过滤器自动配置 .

    import java.util.Collections;
    import java.util.EnumSet;
    import java.util.stream.Collectors;
    
    import javax.servlet.DispatcherType;
    
    import org.springframework.boot.autoconfigure.security.SecurityProperties;
    import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
    
    @Configuration
    public class SecurityFilterConfiguration {
    
        private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
    
        @Bean
        public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
                SecurityProperties securityProperties) {
            DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
                    DEFAULT_FILTER_NAME);
            registration.setOrder(securityProperties.getFilter().getOrder());
            registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
            registration.setUrlPatterns(Collections.singleton("/springpath/*"));
            return registration;
        }
    
        private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
            if (securityProperties.getFilter().getDispatcherTypes() == null) {
                return null;
            }
            return securityProperties.getFilter().getDispatcherTypes().stream()
                    .map((type) -> DispatcherType.valueOf(type.name()))
                    .collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class)));
        }
    }
    

    您可以添加一个简单的测试来验证除非路径以 /springpath/

    @SpringBootTest
    @AutoConfigureMockMvc
    public class ApplicationTests {
        @Autowired
        MockMvc mockMvc;
    
        @Test
        void firewallWhenSpringPathThenEnabled() {
            assertThatExceptionOfType(RequestRejectedException.class)
                    .isThrownBy(() -> mockMvc.perform(request("INVALID", URI.create("/springpath/bar"))));
    
            assertThatExceptionOfType(RequestRejectedException.class)
                    .isThrownBy(() -> mockMvc.perform(request("INVALID", URI.create("/springpath/foo/bar"))));
        }
    
        @Test
        void firewallWhenNotSpringPathThenNotEnabled() {
            assertThatNoException()
                    .isThrownBy(() -> mockMvc.perform(request("INVALID", URI.create("/foo/bar"))));
        }
    }
    

    你可以在 https://github.com/rwinch/spring-security-sample/tree/so-64824460-firewall-subset-requests

    非启动应用程序

    对于不使用Spring Boot的应用程序,它不能利用 过滤注册bean AbstractSecurityWebApplicationInitializer ,但它不允许重写 过滤器链氧 已注册,因为通常不建议使用。这意味着这些应用程序需要创建一个自定义的 WebApplicationInitializer 注册 Filter

    import java.util.EnumSet;
    
    import javax.servlet.DispatcherType;
    import javax.servlet.Filter;
    import javax.servlet.FilterRegistration;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    
    import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
    import org.springframework.util.Assert;
    import org.springframework.web.WebApplicationInitializer;
    import org.springframework.web.filter.DelegatingFilterProxy;
    
    public class SecurityWebApplicationInitializer implements WebApplicationInitializer {
    
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            String filterName = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
            DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(filterName);
            registerFilter(servletContext, filterName, springSecurityFilterChain);
        }
    
        private void registerFilter(ServletContext servletContext, String filterName,
                                    Filter filter) {
            FilterRegistration.Dynamic registration = servletContext.addFilter(filterName, filter);
            Assert.state(registration != null, () -> "Duplicate Filter registration for '" + filterName
                    + "'. Check to ensure the Filter is only configured once.");
            EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
            registration.addMappingForUrlPatterns(dispatcherTypes, true, "/springpath/*");
        }
    
        /**
         * Get the {@link DispatcherType} for the springSecurityFilterChain.
         * @return
         */
        protected EnumSet<DispatcherType> getSecurityDispatcherTypes() {
            return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC);
        }
    }
    

    这个 ApplicationContext 仍然需要注册。通常可以使用 AbstractAnnotationConfigDispatcherServletInitializer . 例如:

    public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return null;
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class[] { ApplicationConfiguration.class };
        }
    
        @Override
        protected String[] getServletMappings() {
            return new String[] { "/" };
        }
    
        @Override
        protected Filter[] getServletFilters() {
            return new Filter[] { new HiddenHttpMethodFilter() };
        }
    
    }
    

    AbstractContextLoaderListenerInitializer 相反。

    import org.springframework.web.context.AbstractContextLoaderInitializer;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    
    public class WebApplicationContextInitializer extends AbstractContextLoaderInitializer {
        @Override
        protected WebApplicationContext createRootApplicationContext() {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(ApplicationConfiguration.class);
            return context;
        }
    }
    

    单元测试使用 MockMvc FilterRegistration

    public class FirewallITests {
    
        private RestTemplate rest;
    
        private int port;
    
        @BeforeEach
        void setup() {
            this.port = Integer.parseInt(System.getProperty("app.httpPort"));
            this.rest = new RestTemplate();
        }
    
        @Test
        void firewallWhenSpringPathThenEnabled() {
            assertThatExceptionOfType(HttpServerErrorException.InternalServerError.class).
                isThrownBy(() -> this.rest.getForObject("http://localhost:" + this.port +"/springpath/;/", String.class));
    
            assertThatExceptionOfType(HttpServerErrorException.InternalServerError.class).
                    isThrownBy(() -> this.rest.getForObject("http://localhost:" + this.port +"/springpath/foo/;/", String.class));
        }
    
        @Test
        void firewallWhenNotSpringPathThenNotEnabled() {
            assertThatNoException().
                    isThrownBy(() -> this.rest.getForObject("http://localhost:" + this.port +"/bar/;/", String.class));
        }
    
    }
    

    https://github.com/rwinch/spring-security-sample/tree/so-64824460-firewall-subset-requests-non-boot