如何使用带有多个过滤器和 Spring Security 的 Spring DelegatingFilterProxy?

Posted

技术标签:

【中文标题】如何使用带有多个过滤器和 Spring Security 的 Spring DelegatingFilterProxy?【英文标题】:How to use Spring DelegatingFilterProxy with multiple filters and Spring Security? 【发布时间】:2015-07-23 21:58:17 【问题描述】:

我们使用的是使用 AbstractSecurityWebApplicationInitializer 初始化的 Spring 安全性。我们还有一个从 AbstractAnnotationConfigDispatcherServletInitializer 扩展的单独的 Web 应用初始化程序。按照前者的 Javadoc 中的建议,我将后者设置为 @Order(Ordered.HIGHEST_PRECEDENCE)。到目前为止一切顺利。

现在我想介绍额外的 Servlet 过滤器,这些过滤器与 Spring 安全性无关,因此应该单独配置。我知道我可以使用DelegatingFilterProxy 将请求委托给过滤器。但是DelegatingFilterProxy 没有接受多个过滤器的能力。 一种选择是定义一个自定义的FilterChain,就像在 Spring Security FilterChainProxy 中所做的那样。这仍然会创建 2 个DelegatingFilterProxy,我知道应用程序中应该只有一个DelegatingFilterProxy

有什么想法吗?

【问题讨论】:

【参考方案1】:

回答我自己的问题,我就是这样做的:

我继承了AbstractAnnotationConfigDispatcherServletInitializer 并在其中:

/**
     * @inheritDoc
     */
    @Override
    protected Filter[] getServletFilters() 
        return new Filter[]  requestContextFilter(), teFilterChain() ;
    

    /**
     * @inheritDoc
     */
    @Override
    protected FilterRegistration.Dynamic registerServletFilter(
            ServletContext servletContext, Filter filter) 
        String filterName = Conventions.getVariableName(filter);
        Dynamic registration = servletContext.addFilter(filterName, filter);
        if (registration == null) 
            int counter = -1;
            while (counter == -1 || registration == null) 
                counter++;
                registration = servletContext
                        .addFilter(filterName + "#" + counter, filter);
                Assert.isTrue(counter < 100, "Failed to register filter '"
                        + filter + "'."
                        + "Could the same Filter instance have been registered already?");
            
        
        registration.setAsyncSupported(isAsyncSupported());

        registration.addMappingForServletNames(getDispatcherTypes(), false,
                getServletName());
        return registration;
    

    /**
     * Spring, by default, registers filters for 'FORWARD' dispatcher type as
     * well which causes TE filter chain to run again after Spring security
     * successfully authenticates and forwards the incoming request. We thus
     * exclude 'FORWARD' dispatcher type.
     */
    private EnumSet<DispatcherType> getDispatcherTypes() 
        return (isAsyncSupported()
                ? EnumSet.of(DispatcherType.REQUEST, DispatcherType.INCLUDE,
                        DispatcherType.ASYNC)
                : EnumSet.of(DispatcherType.REQUEST, DispatcherType.INCLUDE));
    

    private Filter requestContextFilter() 
        return new DelegatingFilterProxy(TE_REQ_CTX_FILTER);
    

    private Filter teFilterChain() 
        return new DelegatingFilterProxy(TE_FILTER_CHAIN);
    

    @Override
    protected void registerDispatcherServlet(ServletContext ctx) 
        super.registerDispatcherServlet(ctx);

        registerSpringSecurityFilter(ctx);
        registerCorsFilter(ctx);
    

    @Override
    protected DispatcherServlet createDispatcherServlet(
            WebApplicationContext servletAppContext) 
        DispatcherServlet servlet = new DispatcherServlet(servletAppContext);
        servlet.setThreadContextInheritable(true);
        servlet.setThrowExceptionIfNoHandlerFound(true);

        return servlet;
    

    private Dynamic registerCorsFilter(ServletContext ctx) 
        Dynamic registration = ctx.addFilter("CorsFilter", CorsFilter.class);

        registration.setInitParameter(CorsFilter.PARAM_CORS_ALLOWED_ORIGINS,
                CORS_ALLOWED_ORIGINS);
        registration.setInitParameter(CorsFilter.PARAM_CORS_ALLOWED_METHODS,
                CORS_ALLOWED_METHODS);
        registration.setInitParameter(CorsFilter.PARAM_CORS_ALLOWED_HEADERS,
                CORS_ALLOWED_HEADERS);

        registration.addMappingForUrlPatterns(getDispatcherTypes(), false,
                "/*");

        return registration;
    

    @Override
    protected boolean isAsyncSupported() 
        return true;
    

    private final FilterRegistration.Dynamic registerSpringSecurityFilter(
            ServletContext servletContext) 
        FilterRegistration.Dynamic registration = servletContext.addFilter(
                SPRING_SECURITY_FILTER_CHAIN, springSecurityFilterChain());

        if (registration == null) 
            throw new IllegalStateException("Duplicate Filter registration for "
                    + SPRING_SECURITY_FILTER_CHAIN
                    + "'. Check to ensure the Filter is only configured once.");
        

        registration.setAsyncSupported(isAsyncSupported());
        EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
        /*
         * Don't use URL mapping for registering Spring security because then
         * the chain will kick in before DispatcherServlet.
         */
        registration.addMappingForServletNames(dispatcherTypes, true,
                getServletName());

        return registration;
    

    private Filter springSecurityFilterChain() 
        return new DelegatingFilterProxy(SPRING_SECURITY_FILTER_CHAIN);
    

    protected EnumSet<DispatcherType> getSecurityDispatcherTypes() 
        return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR,
                DispatcherType.ASYNC);
    

【讨论】:

你不能使用FilterChainProxy吗? @lqbweb 3.5 年后,我不记得了。

以上是关于如何使用带有多个过滤器和 Spring Security 的 Spring DelegatingFilterProxy?的主要内容,如果未能解决你的问题,请参考以下文章

如何在带有自定义过滤器的 Spring Data mongodb 中使用分页和排序?

带有 spring-boot 和 spring-security 的 JWT

Spring Boot 安全性 - 两个不同的过滤器

如何使用带有 gradle 的多个嵌入式服务器运行 spring-boot 集成测试

带有 DRF 的 Django-filter - 如何在使用相同查找应用多个值时执行“和”?

如何在 Spring Boot WebFlux 上使用 AWS X-Ray 跟踪传入请求?