Spring MVC的DelegatingFilterProxy有什么作用?

125

我在我的Spring MVC应用程序的web.xml中看到了这个:

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

我试图弄清楚它为什么存在以及它是否真的有必要。
我在 Spring 文档中找到了这个解释,但它并没有帮助我理解:
它似乎暗示这个组件是将在 web.xml 中定义的 Servlet 和在 Spring 的 applicationContext.xml 中定义的组件之间的 "粘合剂"。

7.1 DelegatingFilterProxy

When using servlet filters, you obviously need to declare them in your web.xml, or they will be ignored by the servlet container. In Spring Security, the filter classes are also Spring beans defined in the application context and thus able to take advantage of Spring's rich dependency-injection facilities and lifecycle interfaces. Spring's DelegatingFilterProxy provides the link between web.xml and the application context.

When using DelegatingFilterProxy, you will see something like this in the web.xml file:

<filter>
   <filter-name>myFilter</filter-name>
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
   <filter-name>myFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

Notice that the filter is actually a DelegatingFilterProxy, and not the class that will actually implement the logic of the filter. What DelegatingFilterProxy does is delegate the Filter's methods through to a bean which is obtained from the Spring application context. This enables the bean to benefit from the Spring web application context lifecycle support and configuration flexibility. The bean must implement javax.servlet.Filter and it must have the same name as that in the filter-name element. Read the Javadoc for DelegatingFilterProxy for more information

因此,如果我从我的web.xml中删除这个,会发生什么?我的servlet将无法与Spring容器通信吗?**

7个回答

132

这里有一些神奇的地方,但最终一切都是一个确定性程序。

DelegatingFilterProxy是一个过滤器,如上所述,它的目标是“委托给实现了Filter接口的Spring管理bean”,也就是说,它在您的Spring应用程序上下文中找到一个名为“target bean”或“delegate”的bean,并调用该bean。它是如何做到的?因为此bean实现了javax.servlet.Filter,所以会调用其doFilter方法。

哪个bean被调用了?DelegatingFilterProxy“支持一个“targetBeanName”[...],指定Spring应用程序上下文中目标bean的名称。”

正如您在web.xml中看到的那样,bean的名称为“springSecurityFilterChain

所以,在Web应用程序的上下文中,过滤器会实例化一个名为“springSecurityFilterChain”的bean,然后通过doFilter()方法委托给它。

请记住,您的应用程序上下文是使用所有应用上下文(XML)文件定义的。例如:applicationContext.xml和applicationContext-security.xml。

因此,尝试在后者中找到一个名为“springSecurityFilterChain”的bean...

...而可能找不到(例如,如果您按照教程操作或使用Roo配置了安全性)

这就是其中的奥妙:有一种新的元素用于配置安全性,类似于

<http auto-config="true" use-expressions="true"> 

只要遵循http://www.springframework.org/schema/security/spring-security-3.0.xsd中允许的方式,就能解决问题。

当Spring使用XML文件加载应用程序上下文时,如果找到一个<security:http>元素,它将尝试设置HTTP安全性,即过滤器栈和受保护的URL,并注册名为“springSecurityFilterChain”的FilterChainProxy。

或者,您可以按照传统方式定义bean,即:

<beans:bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">

但这不是最推荐的方法,因为您需要进行大量配置(涉及所有您将要使用的过滤器,而且这些过滤器超过十几个)。


"applicationContext-security.xml 和 applicationContext-security.xml" 是两次相同的文件名。 - musiKk
谢谢 musiKk(我认为你可以直接编辑帖子) - jbbarquero
这就是我一直在寻找的解释,为我澄清了事情。 - user871611
@jbbarquero:你说得对,但我不确定正确的版本应该是什么。我倾向于让原作者修复,以免无意中改变含义。 - musiKk
好的。无论如何,我非常感谢您帮助我改善我的回答。再次感谢,musiKk。 - jbbarquero
显示剩余2条评论

84
你知道什么是Servlet Filter以及它如何工作吗?它是Servlet规范中非常有用的一部分,允许我们将AOP-like概念应用于HTTP请求服务。许多框架使用过滤器实现各种功能,并且很容易编写和使用自定义的实现。在Spring应用程序中,大多数应用程序功能都在Spring bean中实现。但是,Filter实例由Servlet容器控制。容器实例化、初始化和销毁它。Servlet规范不需要任何Spring集成,因此您只能得到一个非常有用的概念(Filters),没有方便的方法将其与Spring应用程序和执行工作的bean联系起来。
这时就需要DelegatingFilterProxy登场了。您编写一个Filter实现并将其作为Spring bean,但是不要将自己的Filter类添加到web.xml中,而是使用DelegatingFilterProxy,并在Spring上下文中给它您的filter的bean名称。 (如果您没有明确提供名称,则使用“filter-name”)。然后在运行时,DelegatingFilterProxy处理查找实际实现 - 您在Spring中编写和配置的实现 - 并将请求路由到它的复杂性。因此,在运行时,就像您已经在web.xml中列出了过滤器一样,但您可以像任何其他Spring bean一样进行连接。
如果您从web.xml中删除该过滤器映射,则一切都将继续工作,但是您的所有URL都不会受到保护。(假设名称“springSecurityFilterChain”准确描述了其功能。)这是因为此映射正在过滤每个传入请求并将其移交给在Spring上下文中定义的安全过滤器。

感谢您发布这个启发性的评论。我正在学习Spring Security,试图理解它以足够进行自定义。我不知道什么是servlet过滤器或者什么是Spring过滤器。您提到的AOP让人清楚明白为什么要使用过滤器而不仅仅使用servlet......因此您不必在每个servlet/资源中反复编写相同的前/后处理。 - Steve
哇,那个解释正是我所需要的。感谢您分享您的知识。 - Charles Morin
@Ryan Stewart 如果我在应用程序上下文中有两个实现了Filter接口的bean,并且我想按顺序执行它们,那么我该怎么做? - Abhishek Nayak
@skaffman如果我在应用程序上下文中有两个实现了Filter接口的bean,并且我希望按照顺序执行它们,那么我该如何做呢? - Abhishek Nayak

55

什么是Servlet过滤器?

Servlet过滤器是Java WebApp概念中的一种。无论您的应用程序是否使用Spring框架,都可以在任何WebApp中使用servlet过滤器。

这些过滤器可以在请求到达目标servlet之前拦截请求。您可以在servlet过滤器中实现常见功能,如授权。实现后,您可以在web.xml中配置该过滤器,将其应用于特定的servlet、特定的请求URL模式或所有URL模式。

Servlet过滤器的使用场景

现代Web应用程序可能有数十个这样的过滤器。像授权、缓存、ORM会话管理和依赖注入等功能通常都是通过Servlet过滤器实现的。所有这些过滤器都需要在 web.xml 中注册。

实例化Servlet过滤器 - 不使用Spring Framework

您的Servlet容器将创建在web.xml中声明的过滤器实例,并在适当的时候(即服务Servlet请求时)调用它们。如果您与大多数依赖注入(DI)粉丝一样,那么您可能会说,创建实例是我的DI框架(Spring)更擅长的事情。我能不能让我的Servlet过滤器使用Spring创建,以便它们适用于所有DI优点呢?

DelegatingFilterProxy,使Spring创建您的过滤器实例

这就是DelegatingFilterProxy的作用。DelegatingFilterProxy是由Spring Framework提供的javax.servlet.Filter接口的实现。一旦您在web.xml中配置了DelegatingFilterProxy,您可以在Spring配置文件中声明实际执行过滤的bean。这样,Spring将创建执行实际过滤的bean的实例,并且您可以使用DI来配置这些bean。

请注意,您只需要在web.xml中声明一个DelegatingFilterProxy,但您可以在应用程序上下文中链接多个过滤bean


非常好的解释。 - user4906240

19

问题在于,Servlet 过滤器是由 Servlet 容器管理的,而不是由 Spring 管理。您可能需要将一些 Spring 组件注入到您的过滤器中。

因此,如果您需要像下面这样的东西:

public class FooFilter {

    @Inject
    private FooService service;

    public void doFilter(....) { .. }

}

那么你需要使用委派过滤器代理。


1

关于“粘合”东西,您是正确的。正如FilterChainProxy的JavaDocs中所写:

通过在应用程序web.xml文件中添加标准的Spring DelegatingFilterProxy声明,将FilterChainProxy链接到servlet容器过滤器链中。

请参阅博客Behind the Spring Security Namespace中的FilterChainProxy部分,以获得出色的解释。


0

虽然已经过了很长时间,但我有同样的问题,并找到了这个链接:https://www.javacodegeeks.com/2013/11/spring-security-behind-the-scenes.html

我尝试通过删除相关的过滤器或添加它来运行我的Spring Security项目。我发现只有在添加过滤器后,调用才会重定向到在Spring Security配置中定义的所需登录页面。

因此,我同意@Ryan的答案。


0

我一直对web.xml中的“springSecurityFilterChain”感到困惑,后来在springframework安全文档中找到了答案:

<http>元素封装了应用程序Web层的安全配置。它创建了一个名为“springSecurityFilterChain”的FilterChainProxy bean,该bean维护组成Web安全配置的安全过滤器堆栈[19]。一些核心过滤器始终会被创建,而其他过滤器将根据存在的属性子元素添加到堆栈中。标准过滤器的位置是固定的(请参见命名空间介绍中的过滤器顺序表),这消除了以前版本框架的常见错误源,当用户必须在FilterChainProxy bean中显式配置过滤器链时。当然,如果您需要完全控制配置,则仍然可以执行此操作。

这里是链接http://docs.spring.io/spring-security/site/docs/3.0.x/reference/appendix-namespace.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接