The concept of Filter should not be unfamiliar to everyone, especially for those who begin to learn Java from Servlet. Here, we will show how to customize a Filter in Spring Boot projects.

Introduction to Filter

What can we do with Filter? 

Filter is mainly used to filter user requests. It allows us to pre-process and post-process user requests, such as implementing URL-level permission control, filtering illegal requests, and so on. The filter is a specific implementation of aspect-oriented programming-AOP.

The filter is dependent on the Servlet container, and the Filterinterface is under the Servlet package, which is part of the Servlet specification. 

If we need to customize a Filter, it’s very simple, only need to implement javax.Servlet.Filter interface, then override three methods can be inside.

Filter.java

public interface Filter {

   //do some init jobs
    default void init(FilterConfig filterConfig) throws ServletException {
    }
   // do soem filter to the request
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
   // do some job, such as collecting resource 
    default void destroy() {
    }
}

How does Filter implement interception?

In the Filter interface, there is a known doFiltermethod. This method is used to filter the user’s request. The specific process looks like this:

  1. The user sends a request to the webserver, and the request will go to the filter first;
  2. The filter performs some processing on the request, such as filtering the parameters of the request, modifying the content of the response returned to the client, determining whether to allow the user to access the interface and so on.
  3. The user request is complete.
  4. Do something else you want.
Filter implement interception
Process of Filter works

How to customize Filter?

In this post, we will show how to customize Filter by implementing the Filter interface.

In this example, we create two customed Filter and set a order to let them work.

Filter1.java

package com.etbye.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

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

@Component
public class Filter1 implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(Filter1.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("init the filter1.");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("Filter1 begin to pre deal with the request.");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String requestUri = request.getRequestURI();
        System.out.println("requestUri-1: " + requestUri);

        long startTime = System.currentTimeMillis();
        filterChain.doFilter(servletRequest, servletResponse);
        long endTime = System.currentTimeMillis();
        System.out.println("The request cost time-1: " + (endTime - startTime));
    }

    @Override
    public void destroy() {
        logger.info("destroy the filter1.");
    }
}

Filter2.java

package com.etbye.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

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

@Component
public class Filter2 implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(Filter2.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("init the filter2.");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("Filter2 begin to pre deal with the request.");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String requestUri = request.getRequestURI();
        System.out.println("requestUri-2: " + requestUri);

        long startTime = System.currentTimeMillis();
        filterChain.doFilter(servletRequest, servletResponse);
        long endTime = System.currentTimeMillis();
        System.out.println("The request cost time-2: " + (endTime - startTime));
    }

    @Override
    public void destroy() {
        logger.info("destroy the filter2.");
    }
}

Then, we need to register the customed filters in the configuration. In the following code, we register two customed filters and set the work in order.

package com.etbye.config;

import com.etbye.filter.Filter1;
import com.etbye.filter.Filter2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.Arrays;

@Configuration
public class CustomFilterConfig {

    @Autowired
    Filter1 filter1;

    @Autowired
    Filter2 filter2;

    @Bean
    public FilterRegistrationBean<Filter1> customFilter1() {
        FilterRegistrationBean<Filter1> filterRegistrationBean = new FilterRegistrationBean<>();
        //set filter1 works in the second order.
        filterRegistrationBean.setOrder(2);
        filterRegistrationBean.setFilter(filter1);
        filterRegistrationBean.setUrlPatterns(new ArrayList<>(Arrays.asList("/api/*")));

        return filterRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean<Filter2> customFilter2() {
        FilterRegistrationBean<Filter2> filterRegistrationBean = new FilterRegistrationBean<>();
        // set filter2 works at the first order.
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.setFilter(filter2);
        filterRegistrationBean.setUrlPatterns(new ArrayList<>(Arrays.asList("/api/*")));

        return filterRegistrationBean;
    }
}

Method 2:

In fact, you can also let the customed filters auto-configured by adding @WebFilter annotations to it. Then, no need to create a configure class.

For example:

@WebFilter(filterName = "Filter1WithAnnotation", urlPatterns = "/api/*")
public class Filter1WithAnnotation implements Filter {

   ......
}

Note that: if you create customed filters by this method, remember to add @ServletComponentScan annotations to the boot class.

To verify the customed filters in a Controller

FilterTestController.java

package com.etbye.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class FilterTestController {

    @GetMapping("/hello")
    public String getHelloMesg() throws InterruptedException {
        Thread.sleep(1000);
        return "Hello Filter.";
    }
}

Run this Spring Boot project, you will see the log info like this:

2020-03-21 15:56:11.743  INFO 76696 --- [  restartedMain] com.etbye.filter.Filter1                 : init the filter1.
2020-03-21 15:56:11.743  INFO 76696 --- [  restartedMain] com.etbye.filter.Filter2                 : init the filter2.
...
2020-03-21 15:56:18.188  INFO 76696 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-03-21 15:56:18.188  INFO 76696 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-03-21 15:56:18.195  INFO 76696 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 7 ms
2020-03-21 15:56:18.202  INFO 76696 --- [nio-8080-exec-1] com.etbye.filter.Filter2                 : Filter2 begin to pre deal with the request.
requestUri-2: /api/hello
2020-03-21 15:56:18.203  INFO 76696 --- [nio-8080-exec-1] com.etbye.filter.Filter1                 : Filter1 begin to pre deal with the request.
requestUri-1: /api/hello
The request cost time-1: 1045
The request cost time-2: 1045