spring-mvc - 如何将 Spring DelegatingFilterProxy 与多个过滤器和 Spring Security 一起使用?

标签 spring-mvc filter spring-security

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

现在我想介绍额外的 Servlet 过滤器,它们与 Spring 安全性无关,因此应该单独配置。我知道我可以使用 DelegatingFilterProxy 将请求委托(delegate)给过滤器。但是 DelegatingFilterProxy 没有接受多个过滤器的能力。 一种选择是定义自定义 FilterChain,如 Spring Security 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);
    }

关于spring-mvc - 如何将 Spring DelegatingFilterProxy 与多个过滤器和 Spring Security 一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30200841/

相关文章:

java - Spring MVC - 在单个 @requestmapping 中处理多个提交按钮

java - 如何理解 Spring @ComponentScan

Java 流 : filtering with 2 objects

android - Google Play 中的手机和平板电脑过滤器

facebook - Spring Social facebook + Spring Security

java - spring 和 MessagesSource 空指针异常

javascript - 不带管道的过滤器 Angular 6 : overriding problem

java - 在 Spring Boot 中使用 @EnableWebFluxSecurity 时出错

ajax - 将 Spring Security 与 Ajax 调用集成

java - 使Jackson在序列化时不输出类名(使用Spring MVC)