servlet 过滤器在 Spring 中如何工作?

Posted

技术标签:

【中文标题】servlet 过滤器在 Spring 中如何工作?【英文标题】:How do servlet filters work in Spring? 【发布时间】:2015-04-02 08:00:40 【问题描述】:

我正在尝试向我的 Spring Web 应用程序添加一个 CORS 过滤器,但该过滤器没有被执行。我已经按照此处的相关步骤操作:https://spring.io/guides/gs/rest-service-cors/ 无济于事。我没有使用 Spring Boot。我在 3.0+ servlet 规范容器中使用 Spring 的 WebApplicationInitializer 引导我的应用程序。

我的应用程序中的其他一切都在工作:配置类、控制器、休眠实体等。

更新 1:

通过将过滤器添加到 servlet 容器,以下答案对我有用:

container.addFilter("CorsFilter", CorsFilter.class)
          .addMappingForUrlPatterns(null, false, "/*");

但是,我很好奇为什么 Spring Boot 不需要这个?他们必须自动搜索过滤器并将它们添加到上下文中。有没有一种简单的方法可以做到这一点?不幸的是,我必须创建一个过滤器,然后将其添加到另一个类的某个列表中。如果它们像在 Spring Boot 中那样自动注册就好了。

相关代码sn-ps:

WebApplicationInitializer:

public class AppInitializer implements WebApplicationInitializer 

    @Override
    public void onStartup(ServletContext container) throws ServletException 
        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext =
                new AnnotationConfigWebApplicationContext();
        rootContext.register(AppConfig.class);

        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));

        // Create the dispatcher servlet's Spring application context
        AnnotationConfigWebApplicationContext dispatcherContext =
                new AnnotationConfigWebApplicationContext();
        dispatcherContext.register(WebAppConfig.class);

        // Register and map the dispatcher servlet
        ServletRegistration.Dynamic dispatcher =
                container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    


应用配置:

@Configuration
@ComponentScan
public class AppConfig 

WebAppConfig:

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
@ComponentScan(basePackageClasses = AppConfig.class)
public class WebAppConfig extends WebMvcConfigurerAdapter 

CorsFilter:

@Component
public class CorsFilter implements Filter 

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException 

        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        httpResponse.setHeader("Access-Control-Max-Age", "3600");
        httpResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type");

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String origin = httpRequest.getHeader("Origin");
        if (StringUtils.hasText(origin)) 

            boolean isMyDomain =
                Pattern.compile("^https://(.*?\\.)?mydomain.com(:\\d+)?$")
                        .matcher(origin)
                        .find();
            if (isMyDomain) 
                httpResponse.setHeader("Access-Control-Allow-Origin", origin);
             else 
                httpResponse.setHeader("Access-Control-Allow-Origin", "mydomain");
            

        
        chain.doFilter(request, response);
    

    @Override
    public void init(FilterConfig filterConfig) throws ServletException 
        LoggerFactory.getLogger(getClass()).info("CorsFilter initiated");
    

    @Override
    public void destroy() 
        LoggerFactory.getLogger(getClass()).info("CorsFilter destroyed");
    

【问题讨论】:

您不应该在 Web 容器中注册您的过滤器(例如,通过将其添加到 web.xml) How to write a custom filter in spring security?的可能重复 【参考方案1】:

您只需在AppInitializer 中注册您的过滤器。

public class AppInitializer implements WebApplicationInitializer 

    @Override
    public void onStartup(ServletContext container) throws ServletException 
        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext =
                new AnnotationConfigWebApplicationContext();
        rootContext.register(AppConfig.class);

        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));

        // Create the dispatcher servlet's Spring application context
        AnnotationConfigWebApplicationContext dispatcherContext =
                new AnnotationConfigWebApplicationContext();
        dispatcherContext.register(WebAppConfig.class);

        // Register and map the dispatcher servlet
        ServletRegistration.Dynamic dispatcher =
                container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");

        //Added filter dynamically
        javax.servlet.FilterRegistration.Dynamic corsFilter = container.addFilter("corsfilter", CORSFilter.class);
        corsFilter.addMappingForUrlPatterns(null, true, "/*");
    


您可以参考this github repository。

【讨论】:

好的,非常感谢,我会试一试...出于好奇,Spring Boot 如何在不显式在 WebApplicationInitializer 中添加过滤器的情况下做到这一点?使用 Spring Boot,我只需将 javax.servlet.Filter 添加为 spring bean,其余的就交给它了。 在docs.spring.io/spring-boot/docs/current/reference/html/… 的文档中的 26.3.1 Servlet 和 Filters 下,它表示:“使用嵌入式 servlet 容器时,您可以将 Servlet 和 Filters 直接注册为 Spring bean。”【参考方案2】:

你可以这样添加:

container.addFilter("CorsFilter", CorsFilter.class)
          .addMappingForUrlPatterns(null, false, "/*");

【讨论】:

我已经更新了关于 Spring Boot 自动注册 servlet 过滤器的方法的问题。

以上是关于servlet 过滤器在 Spring 中如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring/Servlets 支持批量 Web api 请求处理

详解Spring mvc工作原理及源码分析

我们可以在 Spring Webflux 中使用 web servlet 和 servlet 过滤器吗?

Spring Security:在 servlet 过滤器中访问当前经过身份验证的用户

在 Spring Boot 中添加 servlet 过滤器时没有默认构造函数

Zuul开发人员指南