Using Filters in Spring Web Applications
0 CommentsSpring Web applications and RESTful services contain controllers responsible to process requests and send back responses. At times you might need to perform certain operations on client requests before it reaches the controller. Similarly, you might need to perform operations on responses sent back by controllers to clients. You can achieve this using filters in Spring Web applications.
Filters are implementations of the Filter
interface of Java EE. Spring brings in its own filter implementation with the GenericFilterBean
abstract class.
Some of the common use cases of filters are:
- Logging requests and response
- Logging request processing time
- Formatting of request body or header
- Verifying authentication tokens
- Compressing response
- Performing Image conversions
In this post, you will learn how to configure filters in Spring Boot applications.
Filter Interface Methods
The Filter
Interface contains the following three methods:
init()
: The web container calls this method to indicate to a filter that it is being placed into service. The container calls this method only once. during the lifecycle of the filter instance. Theinit()
method must complete successfully before the filter is asked to do any filtering work. The web container cannot place the filter into service if theinit()
method either:- Throws a
ServletException
- Does not return within a time period defined by the web container
- Throws a
doFilter()
: The Web container invokes this method every time whenever the client sends a request or the application sends back a response. It is in this method where you perform operation on the request and response objects.destroy()
: The Web container calls this method to indicate to a filter that it is being taken out of service. The container calls this method only once during the lifecycle of the filter instance. This method gives the filter an opportunity to clean up any resources that are being held. For example, memory, file handles, and threads.
Note: The GenericFilterBean
abstract class of Spring implements the Filter
interface. The class leaves actual filtering to subclasses, which have to implement the doFilter()
method.
Filter Example
This example demonstrates configuring filters in Spring Web applications.
Maven Dependency
For this demo, you will need the spring-boot-starter-web
and lombok
dependencies in your pom.xml
.
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency>
The Domain Class
The code of the MemeMaker
domain class is this.
Mememaker.java
@Getter @Setter public class MemeMaker { private int memeId; private String memeMaker; private String memeTopic; private String memeLevel; }
The preceding code uses Lombok
to reduce boilerplate code. If you are new to Lombok
, I suggest going through my post on Lombok.
This is code for the MemeController
class.
MemeController.java
@RestController @RequestMapping("/meme") public class MemeController { @GetMapping @ResponseBody public MemeMaker getMemeMakerDetails() { MemeMaker memeMaker = new MemeMaker(); memeMaker.setMemeId(1); memeMaker.setMemeMaker("Alex"); memeMaker.setMemeLevel("Noobie"); memeMaker.setMemeTopic("Trending"); return memeMaker; } }
The preceding code annotates the controller class with @RestController
. It has one handler method getMemeMakerDetails()
for GET request. This method returns a MemeMaker
object.
The Filter Class
The next step is to create a filter, like this
MemeFilter.java
package guru.springframework.springfilter.filter; import ch.qos.logback.classic.Level; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component @Slf4j public class MemeFilter implements Filter { @Override public void init(FilterConfig filterConfig) { log.debug("init() method has been get invoked"); log.debug("Filter name is "+filterConfig.getFilterName()); log.debug("ServletContext name is"+filterConfig.getServletContext()); log.debug("init() method is ended"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { log.debug("doFilter() method is invoked"); HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest; HttpServletResponse httpServletResponse = (HttpServletResponse)servletResponse; filterChain.doFilter(httpServletRequest, httpServletResponse); log.debug("doFilter() method is ended"); } @Override public void destroy() { log.debug("destroy() method is invoked"); } }
The preceding code creates a filter class named MemeFilter
that implements the Filter
interface. The code annotates the class with @Component
so that Spring detects it during component scanning. In addition, the MemeFilter
class overrides the methods of the Filter
interface to access the request and response objects and log information. To set the log level for logging to the console, add the following configuration to your application.properties file.
logging.level.guru.springframework=DEBUG
On running the application, the container invokes the init()
method. However, the container is yet to invoke thedoFilter()
method.
Open a browser and access http://localhost:8080/meme. This invokes the doFilter()
method.
Finally, stop the application. This invokes the destroy()
method.
Filter Use Cases in Spring
The Spring Framework provides the GenericFilterBean class to configure filters in Spring Web applications. This class is a Spring specific base implementation of the Filter
interface. Let us look how to use GenericFilterBean
to perform some common operations in filters.
Verifying Authentication Tokens
JSON Web Tokens (JWT) is one of the common authentication mechanism in Spring Boot REST services. In this type of authentication, client sends a JWT token to access a service. If you are working with microservices, instead of validating the token in each service, you can offload it to a filter. Such filter can intercept the request and validate the token before passing the request to a service for processing.
The following code shows an example of such filter.
package guru.springframework.springfilter.filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.filter.GenericFilterBean; import java.io.IOException; import io.jsonwebtoken.*; public class JwtFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) req; final HttpServletResponse response = (HttpServletResponse) res; final String authHeader = request.getHeader("authorization"); if ("OPTIONS".equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); filterChain.doFilter(req, res); } else { if (authHeader == null || !authHeader.startsWith("Bearer ")) { throw new ServletException("Missing or invalid Authorization header"); } final String token = authHeader.substring(7); final Claims claims = Jwts.parser() .setSigningKey("secretkey") .parseClaimsJws(token) .getBody(); request.setAttribute("claims", claims); filterChain.doFilter(req, res); } } }
Logging Request Processing Time
You can use filters to log request processing time.
The following code shows an example of such a filter.
package guru.springframework.springfilter.filter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.filter.GenericFilterBean; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @Component @Slf4j public class RequestProcessingTimeFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)throws IOException, ServletException { long time = System.currentTimeMillis(); try { filterChain.doFilter(req, res); } finally { time = System.currentTimeMillis() - time; log.debug("Request was processed in: {}: {} ms ", ((HttpServletRequest) req).getRequestURI(), time); } } }
On running the application and sending a request, you can see the request processing time in milliseconds on the console.
Filter Ordering in Filter Chain
I have shown configuring multiple filters in a Spring Web application. These filters can together form a filter chain in an application. A request goes through the chain of filters and reach the controller unless a filter throws some exception to stop the request flow.
When you have multiple filters forming a filter chain, you can set the invocation order of the filters. There are two approaches.
If you are using the @Component
annotation in the filter class, you can set the ordering using the @Order
annotation, like this.
@Component @Slf4j @Order(0) public class MemeFilter implements Filter { .... @Order(0) } @Component @Slf4j @Order(1) public class JwtFilter implements Filter { .... } @Component @Slf4j @Order(2) public class RequestProcessingTimeFilter implements Filter { .... }
The preceding configuration will set the filter chain, like this.
The second approach is through Java configuration. In this approach you would have filter beans ordered and defined like this.
package guru.springframework.springfilter.config; import guru.springframework.springfilter.filter.MemeFilter; import guru.springframework.springfilter.filter.RequestProcessingTimeFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterBeanConfig { @Bean public FilterRegistrationBean requestMemeFilter() { MemeFilter memeFilter=new MemeFilter(); final FilterRegistrationBean reg = new FilterRegistrationBean(memeFilter); reg.addUrlPatterns("/*"); reg.setOrder(1); //defines filter execution order return reg; } @Bean public FilterRegistrationBean requestRequestProcessingTimeFilter() { RequestProcessingTimeFilter requestProcessingTimeFilter =new RequestProcessingTimeFilter(); final FilterRegistrationBean reg = new FilterRegistrationBean(requestProcessingTimeFilter); reg.addUrlPatterns("/*"); reg.setOrder(2); //defines filter execution order return reg; } }
Summary
Developers often confuse between filters and Springs handler interceptor as both performs similar functions.
Handler interceptor is basically similar to a Servlet filter, but in contrast to the latter it just allows custom pre-processing with the option of prohibiting the execution of the handler itself. Handler interceptor also allows custom post-processing. Filters are more powerful, for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml
, and handler interceptor in the application context.
As a basic guideline, fine-grained handler-related pre-processing tasks are candidates for handler interceptors, especially factored-out common handler code and authorization checks. On the other hand, a filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.
You can find the source code of this post here on Github.
For in-depth knowledge on filters, you can check my Udemy Best Seller Course Spring Framework 5: Beginner to Guru