如何使用具有多个过滤器和 Spring 安全性的 Spring DelegatingFilterProxy?

How to use Spring DelegatingFilterProxy with multiple filters and Spring Security?

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

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

有什么想法吗?

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

我将 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);
    }