如何在Spring Boot中添加一个过滤器类?

286

在Spring Boot中,用于Web应用程序的Filter类是否有任何注释?也许是@Filter

我想在我的项目中添加自定义过滤器。

Spring Boot参考指南提到了FilterRegistrationBean,但我不确定如何使用它。


1
请注意,使用@WebFilter添加的过滤器不像Servlet规范中的真正过滤器那样运作。它将成为一个Spring bean,在许多其他Spring beans之后被调用,而不是像真正的过滤器在任何servlet代码之前。 - lrxw
你能告诉我您的具体需求吗?如果您想为 ComponentScan 进行过滤,则有一个名为 "@ComponentScan.Filter" 的注释可供使用。 - Keaz
你应该写更多的细节,我们有不同类型的过滤器,例如:用于请求的过滤器,客户端过滤器用于从列表中进行筛选等等。 - ZahraAsgharzade
29个回答

185
如果您想设置第三方过滤器,可以使用FilterRegistrationBean。例如,等价于web.xml的内容:
<filter>
     <filter-name>SomeFilter</filter-name>
        <filter-class>com.somecompany.SomeFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SomeFilter</filter-name>
    <url-pattern>/url/*</url-pattern>
    <init-param>
        <param-name>paramName</param-name>
        <param-value>paramValue</param-value>
    </init-param>
</filter-mapping>

这将是您的@Configuration文件中的两个bean:

@Bean
public FilterRegistrationBean someFilterRegistration() {

    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(someFilter());
    registration.addUrlPatterns("/url/*");
    registration.addInitParameter("paramName", "paramValue");
    registration.setName("someFilter");
    registration.setOrder(1);
    return registration;
}

public Filter someFilter() {
    return new SomeFilter();
}

以上内容已使用Spring Boot 1.2.3进行测试。


2
如果我想添加多个过滤器怎么办?@Opal - verystrongjoe
10
只需添加以下额外的代码: @Bean public FilterRegistrationBean additionalFilterRegistration() - Haim Raman
7
FilterRegistrationBean.setOrder 的翻译: 设置过滤器的优先级。 - Haim Raman
2
示例中缺少 @Bean 注释在 someFilter() 上。 - Pavel Vlasov
2
Filter 在哪个软件包中?我看到有超过 24 个可能的匹配项。也许可以包括一个导入语句片段。 - dlamblin
显示剩余8条评论

132

以下是在Spring Boot MVC应用程序中包含自定义过滤器的一种方法示例。确保在组件扫描中包含该包:

package com.dearheart.gtsc.filters;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

@Component
public class XClacksOverhead implements Filter {

  public static final String X_CLACKS_OVERHEAD = "X-Clacks-Overhead";

  @Override
  public void doFilter(ServletRequest req, ServletResponse res,
      FilterChain chain) throws IOException, ServletException {

    HttpServletResponse response = (HttpServletResponse) res;
    response.setHeader(X_CLACKS_OVERHEAD, "GNU Terry Pratchett");
    chain.doFilter(req, res);
  }

  @Override
  public void destroy() {}

  @Override
  public void init(FilterConfig arg0) throws ServletException {}

}

8
过滤器需要在某处进行注册吗? - gstackoverflow
1
当我尝试那种方法时,Filter 被创建为 bean 甚至被注入到其他类中,但 init() 方法没有运行。可能只有在“正常”注册而不是通过 Spring 容器注册时,init() 才能起作用。我认为可以使用 PostConstruct 替代 init(),但我拒绝将 Filter 声明为 Spring bean,所以没有尝试过。 - user2104560
使用这种方法进行过滤排序怎么样? - Pavel Vlasov
我们如何从ServletResponse中获取RS body? - user2602807
5
需要注意的一件重要事情是,你的bean名称(基于你的类名)不应该与Spring bean相同。例如,你可能会想创建一个MetricsFilter,但这个bean将被同名的Spring执行器bean掩盖。我已经吃过这方面的亏了... - kinbiko
也许更好的方法是扩展 org.springframework.web.filter.GenericFilterBean - Jakub Holý

95

有三种方法可以添加过滤器:

  1. 使用Spring中的任一注释如@Component来注解您的过滤器。
  2. 在Spring的@Configuration中注册Filter类型的@Bean
  3. 在Spring的@Configuration中注册FilterRegistrationBean类型的@Bean

如果您希望过滤器应用于所有请求而不需要自定义,请使用#1或#2。否则,请使用#3。对于#1,只要将过滤器类放在与您的SpringApplication类相同或子包中,就不需要指定组件扫描即可工作。对于#3,仅当您希望Spring管理您的过滤器类(例如自动连接依赖项)时,才需要与#2一起使用。对于我所创建的不需要任何依赖项自动连接/注入的过滤器,直接实例化即可。

虽然组合#2和#3是有效的,但我很惊讶它不会导致两个过滤器重复应用。我猜测Spring在调用相同的方法创建这两个bean时将它们合并为一个。如果您想仅使用#3进行依赖注入,您可以使用AutowireCapableBeanFactory。以下是一个示例:

private @Autowired AutowireCapableBeanFactory beanFactory;

    @Bean
    public FilterRegistrationBean myFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        Filter myFilter = new MyFilter();
        beanFactory.autowireBean(myFilter);
        registration.setFilter(myFilter);
        registration.addUrlPatterns("/myfilterpath/*");
        return registration;
    }

1
非常好的回答。感谢您详细介绍了所有选项,并涵盖了如何在使用FilterRegistrationBean时自动装配您的Filter - sbk
非常好的回答。这正是我想要的! - haykart
1
这也在这里描述:https://www.baeldung.com/spring-boot-add-filter - Jakub Holý

85

没有特别的注解来表示一个Servlet过滤器。您只需声明类型为Filter(或FilterRegistrationBean)的@Bean。一个例子(向所有响应添加自定义标题)在Boot自己的EndpointWebMvcAutoConfiguration中;

如果您只声明了一个Filter,它将应用于所有请求。如果您还添加了一个FilterRegistrationBean,则可以进一步指定要应用的单个servlet和URL模式。

注意:

从Spring Boot 1.4开始,FilterRegistrationBean未被弃用,仅将软件包从org.springframework.boot.context.embedded.FilterRegistrationBean移动到org.springframework.boot.web.servlet.FilterRegistrationBean


你能告诉我如何在build.gradle中包含相应的条目吗? 我添加了以下内容,但它没有被编译: providedCompile('javax.servlet:servlet-api:2.5') runtime('javax.servlet:jstl:1.1.2') - janetsmith
3
Spring Boot不支持Servlet 2.5,并且对JSP的支持也不是很好。我不是很了解Gradle,所以我不知道你想做什么。 "compile"有什么问题?如果你只依赖于"spring-boot-starter-web",它能工作吗?(不过我认为这些问题与原问题无关,也许你应该再次发布带有新问题的帖子?) - Dave Syer
我通过实现Filter接口添加了一个过滤器,但是Eclipse找不到这个接口。因此,我正在努力弄清楚如何将它添加到类路径以进行编译。 - janetsmith
1
当然,您需要在类路径上拥有 Filter。通常我会使用 spring-boot-starter-web 将所有相关的依赖项都引入进来(例如这里)。 - Dave Syer
如其他答案所述,您也可以将您的Filter类注释为@Component,它将自动注册(适用于所有URL)。 - Jakub Holý

45

更新:2022-05-29:

在Spring Boot 1.5.8.RELEASE中有两种简单的方法可以实现此操作,无需使用XML。

第一种方法:

如果您没有任何特定的URL模式,您可以像这样使用@Component(完整代码和详细信息在此处https://github.com/surasint/surasint-examples/tree/master/spring-boot-jdbi/3_spring-boot-filter,请查看README.txt开始):

@Component
public class ExampleFilter implements Filter {
    ...
}

第二种方法:

如果你想使用URL模式,你可以像这样使用@WebFilter(完整的代码和详细信息在这里:https://github.com/surasint/surasint-examples/tree/master/spring-boot-jdbi/4_spring-boot-filter-urlpattern ,查看README.txt开始):

@WebFilter(urlPatterns = "/api/count")
public class ExampleFilter implements Filter {
    ...
}

但你还需要在 @SpringBootApplication 类中添加 @ServletComponentScan 注解:

@ServletComponentScan
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
    ...
}

请注意,@Component是Spring的注解,但@WebFilter不是。@WebFilter是Servlet 3的注解。

无论哪种方式,您只需要在pom.xml中添加基本的Spring Boot依赖项(不需要显式添加Tomcat嵌入式Jasper)。

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
    </parent>

    <groupId>com.surasint.example</groupId>
    <artifactId>spring-boot-04</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

警告:如果在Spring Boot中Controller返回到JSP文件,第一种方式会导致请求通过过滤器两次。

而第二种方式只会让请求通过过滤器一次。

我更喜欢第二种方式,因为它更类似于Servlet规范中的默认行为。


我曾经看到在applicationContext启动期间多次调用“Filter”接口。有没有办法只执行一次? - PAA
@PAA 你的意思是从我的例子中吗? - Surasin Tancharoen
@Surasin:你的博客上有很棒的文章。问题是,除了在过滤器类上使用 @WebFilter 之外,是否在该类上同时使用 @ComponentScan 就足以让 Spring 应用程序上下文找到它呢? - Saurabh Patil
所有的 www.surasint.com 链接都已经失效(“嗯,我们无法找到该网站。我们无法连接到 www.surasint.com 服务器。”)。 - Peter Mortensen
@PeterMortensen,它消失了。Gcloud已经彻底摧毁了它。而且我没有任何备份 T-T。 - Surasin Tancharoen
你可以在过滤器上使用 @Component,而不是在 App 类上使用 @ServletComponentScan。 - tomtomssi

26

这是我的自定义过滤器类的示例:

package com.dawson.controller.filter;

import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@Component
public class DawsonApiFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        if (req.getHeader("x-dawson-nonce") == null || req.getHeader("x-dawson-signature") == null) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setContentType("application/json");
            httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Required headers not specified in the request");
            return;
        }
        chain.doFilter(request, response);
    }
}

我通过将它添加到 Configuration 类中来将其添加到 Spring Boot 配置中,代码如下:

package com.dawson.configuration;

import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import com.dawson.controller.filter.DawsonApiFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

@SpringBootApplication
public class ApplicationConfiguration {
    @Bean
    public FilterRegistrationBean dawsonApiFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new DawsonApiFilter());

        // In case you want the filter to apply to specific URL patterns only
        registration.addUrlPatterns("/dawson/*");
        return registration;
    }
}

18

从Spring文档中,

嵌入式servlet容器 - 向应用程序添加Servlet、Filter或Listener

要添加Servlet、Filter或Servlet *Listener,请为其提供@Bean定义。

例如:

@Bean
public Filter compressFilter() {
    CompressingFilter compressFilter = new CompressingFilter();
    return compressFilter;
}
将下面的内容翻译为中文:

将此@Bean配置添加到您的@Configuration类中,过滤器将在启动时注册。

您还可以使用类路径扫描添加Servlet、Filter和Listener,

@WebServlet、@WebFilter和@WebListener注解的类可以通过在@Configuration类上注释@ServletComponentScan并指定要注册的组件所在的package(s),自动注册到嵌入式servlet容器中。 默认情况下,@ServletComponentScan将从注释类的包扫描。


通过在@Configuration类上注释@ServletComponentScan,并指定包含要注册的组件的包(s),自动将带有@WebServlet、@WebFilter和@WebListener注解的类注册到嵌入式servlet容器中。默认情况下,@ServletComponentScan将从注释类的包扫描。


http://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-servlet-containers.html#howto-add-a-servlet-filter-or-listener 对我没用,但是 https://docs.spring.io/spring-boot/docs/1.5.8.RELEASE/reference/html/howto-embedded-servlet-containers.html 对我有用。 - user674669
链接已损坏(404)。 - Peter Mortensen

14

我们大约有四个不同的选项来 使用Spring注册过滤器

首先,我们可以创建一个实现Filter或扩展HttpFilter的Spring bean:

@Component
public class MyFilter extends HttpFilter {

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) 
        throws IOException, ServletException {
        // Implementation details...

        chain.doFilter(request, response);
    }
}

其次,我们可以创建一个继承GenericFilterBean的Spring Bean:
@Component
public class MyFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
  throws IOException, ServletException {
    //Implementation details...

        chain.doFilter(currentRequest, servletResponse);
    }
}

或者我们可以使用FilterRegistrationBean类:

@Configuration
public class FilterConfiguration {

    private final MyFilter myFilter;

    @Autowired
    public FilterConfiguration(MyFilter myFilter) {
        this.myFilter = myFilter;
    }

    @Bean
    public FilterRegistrationBean<MyFilter> myFilterRegistration() {
        FilterRegistrationBean<DateLoggingFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(myFilter);
        filterRegistrationBean.setUrlPatterns(Collections.singletonList("/*"));
        filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST);
        filterRegistrationBean.setOrder(Ordered.LOWEST_PRECEDENCE - 1);
        return filterRegistrationBean;
    }
}

最后,我们可以使用@WebFilter注解和@ServletComponentScan

@WebFilter(urlPatterns = "/*", dispatcherTypes = {DispatcherType.REQUEST})
public class MyFilter extends HttpFilter {

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
  throws IOException, ServletException {
        // Implementation details...

        chain.doFilter(request, response);
    }
}

1
使用哪个过滤器以及为什么? - PAA

10
如果您使用Spring Boot + Spring Security,您可以在安全配置中实现这一点。
在下面的示例中,我在UsernamePasswordAuthenticationFilter之前添加了自定义过滤器(请参阅 所有默认的Spring Security过滤器及其顺序)。
@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired FilterDependency filterDependency;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(
                new MyFilter(filterDependency),
                UsernamePasswordAuthenticationFilter.class);
    }
}

过滤器类

class MyFilter extends OncePerRequestFilter  {
    private final FilterDependency filterDependency;

    public MyFilter(FilterDependency filterDependency) {
        this.filterDependency = filterDependency;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
        HttpServletResponse response,
        FilterChain filterChain)
        throws ServletException, IOException {

        // Filter
        filterChain.doFilter(request, response);
    }
}

9
使用@WebFilter注解,可以按照以下方式执行:
@WebFilter(urlPatterns = {"/*" })
public class AuthenticationFilter implements Filter{

    private static Logger logger = Logger.getLogger(AuthenticationFilter.class);

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

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

         logger.info("checking client id in filter");
        HttpServletRequest request = (HttpServletRequest) arg0;
        String clientId = request.getHeader("clientId");
        if (StringUtils.isNotEmpty(clientId)) {
            chain.doFilter(request, response);
        } else {
            logger.error("client id missing.");
        }
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub

    }

}

4
还要添加@ServletComponentScan - Justinas Jakavonis

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