1. Introduction

Sometimes we need to declare some custom Servlet filters in our Spring Boot Servlet Web application to handle some logic. Simple permission systems, request header filtering, prevention of XSS attacks, etc. This article explains how to declare custom Servlet filters in a Spring Boot application and define their respective scopes and order.

2. Customize Filter

Some might say that declaring a Servlet Filter is simply implementing the Filter interface. Yes, that’s true, but many times we don’t want our declared Filter to apply to all requests. Even a request that passes through multiple filters needs to be executed in a given order. I’ll explain how to do all of these things.

2.1 Declaration of Filter

All you need to do in Spring Boot is declare a Spring Bean that implements the Javax.servlet.filter interface. As follows:

@Configuration
public class FilterConfig {


    @Bean
    public Filter requestFilter(a) {
        return (request, response, chain) -> {
            //todo your business
        };
    }

    @Bean
    public Filter responseFilter(a) {
        return (request, response, chain) -> {
            //todo your business}; }}Copy the code

Pretty simple isn’t it? However, this approach does not guarantee order and applies to all requests, with the intercepted Ant rule /*. So we need to improve

2.2 Filter sequencing

If you need to implement ordering, you can use the @Order annotation provided by Spring or the Ordered interface. There’s a catch: if you use @Order annotations, make sure the annotation is on a specific class. To facilitate JavaConfig style declarations. We can implement the OrderedFilter interface, which is a composite of the Filter interface and Ordered interface, and end up with the following configuration

@Configuration
public class FilterConfig {

    @Bean
    public OrderedFilter responseFilter(a) {
        return new ResponseFilter("/foo/*");
    }

    @Bean
    public OrderedFilter requestFilter(a) {
        return new RequestFilter("/foo/*"); }}Copy the code

The Filter executes the rule that the smaller the number, the earlier it is executed. The priority is the same as the Bean instantiation priority.

2.3 Customizing the Filter Scope

With sequencalization in place, let’s look at how to implement the scope of a custom Filter. Let’s start with the idea:

A ServletRequest object is used to get the URI of the request, and then the URI is matched in an ANT style, which you can refer to in my article. Match by executing specific logic, otherwise skip the Filter.

This is a good place to anchor the process by abstracting a base class, leaving an abstract method as a function hook, and simply implementing the abstract method hook from the base class. To ensure that the base class is executed sequentially we still implement the OrderedFilter interface. Let’s define the base class:

package cn.felord.springboot.filters;

import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;

/**
 * The type Abstract filter bean.
 *
 * @author Felordcn
 * @since 11 :19
 */
public abstract class AbstractFilterBean implements OrderedFilter {
    private Set<String> urlPatterns = new LinkedHashSet<>();

    public AbstractFilterBean(String... urlPatterns) {
        Collections.addAll(this.urlPatterns, urlPatterns);
    }

    /** * The respective logical function hooks **@param request  the request
     * @param response the response
     */
    public abstract void internalHandler(ServletRequest request, ServletResponse response);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
         // Perform ant matching true to perform specific interception logic false skip
        if (this.antMatch(request)) {
            this.internalHandler(request, response);
        }
        chain.doFilter(request, response);
    }


    private boolean antMatch(ServletRequest request) {
        Set<String> urlPatterns = getUrlPatterns();

        if(! CollectionUtils.isEmpty(urlPatterns)) {// Perform Ant matching
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            String uri = httpServletRequest.getRequestURI();
            Optional<String> any = urlPatterns.stream().filter(s -> {
                AntPathMatcher antPathMatcher = new AntPathMatcher();
                return antPathMatcher.match(s, uri);
            }).findAny();

            return any.isPresent();
        }
        // If there are no elements, all are matched
        return true;
    }


    public Set<String> getUrlPatterns(a) {
        returnurlPatterns; }}Copy the code

Let’s implement a concrete Filter logic that prints the request URI:

@Slf4j
public class RequestFilter extends AbstractFilterBean {

    public RequestFilter(String... urlPatterns) {
        super(urlPatterns);
    }

    @Override
    public void internalHandler(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        log.info("request from {}", httpServletRequest.getRequestURI());
    }

    @Override
    public int getOrder(a) {
       // Define your own priorities
        return 0; }}Copy the code

You then define its urlPatterns and register them in the Spring IoC container, or if you have more than one and want to execute them in a certain order, follow the method provided in Section 2.2.

3. Spring Boot mechanism

The above way is our own wheel. Spring Boot also provides a Filter registration mechanism to implement sequential execution and declare scope. Our logic above can be changed to:

package cn.felord.springboot.configuration;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/** * Use the registration mechanism provided by Spring Boot **@author Felordcn
 * @since14:27 * * /
@Configuration
@Slf4j
public class SpringFilterRegistrationConfig {


    @Bean
    public FilterRegistrationBean<Filter> responseFilter(a) {
        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setName("responseFilter");
        registrationBean.setOrder(2);
        registrationBean.setFilter((request, response, chain) -> {
            HttpServletResponse servletResponse = (HttpServletResponse) response;
            log.info("response status {}", servletResponse.getStatus());
            chain.doFilter(request,response);
        });
        registrationBean.addUrlPatterns("/foo/*");
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean<Filter> requestFilter(a) {
        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setName("requestFilter");
        registrationBean.setOrder(1);
        registrationBean.setFilter((request, response, chain) -> {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            log.info("request from {}", httpServletRequest.getRequestURI());
            chain.doFilter(request,response);
        });
        registrationBean.addUrlPatterns("/foo/*");
        returnregistrationBean; }}Copy the code

3.1 points

  • FilterRegistrationBeanFilterIt’s a one-to-one relationship.
  • If there are multipleFilterRegistrationBeanYou need to call itsetName(String name)Declare a unique name for it, otherwise only the first successful registration is valid.
  • You can call them if you need to ensure the order of the callssetOrder(int order)Method to set.

4. To summarize

In this article, we use custom Filter and Spring Boot to implement two methods. Although Spring Boot provides a more convenient method, the custom method is better to show your understanding of object-oriented and improve your abstraction ability. Keep your eyes open, as always. Follow the public account: Felordcn reply f01 to obtain this article DEMO.