Spring Boot interceptor example

In this article, we will see the Spring Boot interceptor example. Also, we will see how to configure interceptors using spring boot and what is the use of interceptors. These interceptors will get called before the request will reach to controller/endpoint classes.

Define custom interceptor in Spring Boot

Step 1 – Spring provides HandlerInterceptorAdapter class, we can define custom interceptors extending this class.  HandlerInterceptorAdapter is an abstract class. We need to override the below methods in our interceptor.

boolean preHandle() – This method will get called by container while sending the request to controller classes. Typically used, if we want to send some data with the request or if we want to modify the request data.

void postHandle() – This method will get invoked while sending the response from the server. We can write some logic inside this method if want to modify response data.

afterCompletion() – This method is used for resource clean up task.

Step 2 – We need to define a class WebAppConfigAdapter.java which should implement the WebMvcConfigurer interface and need to override the addInterceptors() method. Using the addInterceptors() method we will add our custom interceptors.

@Override
public void addInterceptors (InterceptorRegistry interceptorRegistry) {
System.out.println("securityInterceptor --" +securityInterceptor);
interceptorRegistry.addInterceptor(securityInterceptor);
}

Note – There is another method afterConcurrentHandlingStarted() can be used instead postHandle() and afterCompletion() when handler is executed concurrently. This method is used for clean up thread-local variables.

Spring Boot interceptor example from scratch

Create a maven project, Don’t forget to check ‘Create a simple project (skip)’click on next. Fill all details(GroupId – springbootinterceptor, ArtifactId – springbootinterceptor, and name – springbootinterceptor) and click on finish. Keep packaging as the jar.

maven dependency – pom.xml


<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>
	<groupId>springbootinterceptor</groupId>
	<artifactId>springbootinterceptor</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springbootinterceptor</name>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.2.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
</project>

Our directory structure should be as below.

Spring Boot interceptor example

Book.java

package com.netsurfingzone.entity;

public class Book {
	int bookId;
	String bookName;
	String bookPrice;

	public int getBookId() {
		return bookId;
	}

	public void setBookId(int bookId) {
		this.bookId = bookId;
	}

	public String getBookName() {
		return bookName;
	}

	public void setBookName(String bookName) {
		this.bookName = bookName;
	}

	public String getBookPrice() {
		return bookPrice;
	}

	public void setBookPrice(String bookPrice) {
		this.bookPrice = bookPrice;
	}

}

BooKController.java

package com.netsurfingzone.controller;

import java.util.concurrent.Callable;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.netsurfingzone.entity.Book;

@RestController
@RequestMapping("/book")
public class BookController {

	@RequestMapping(value = "/getbook", method = RequestMethod.GET)
	@ResponseBody
	public Book getBook() {
		Book book = new Book();
		book.setBookId(1);
		book.setBookName("rich dad poor dad");
		book.setBookPrice("10");
		return book;
	}

	@RequestMapping(value = "/getasync", method = RequestMethod.GET)
	public Callable<Book> handleTestRequest() {
		Callable<Book> callable = new Callable<Book>() {
			public Book call() throws Exception {
				Thread.sleep(100);
				Book book = new Book();
				book.setBookId(1);
				book.setBookName("rich dad poor dad");
				book.setBookPrice("10");
				return book;
			}
		};
		return callable;
	}
}

SpringMainExample.java

package com.netsurfingzone.main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = "com.netsurfingzone.*")
public class SpringMain {
	public static void main(String[] args) {

		SpringApplication.run(SpringMain.class, args);
	}

}

Now we need to define two classes for interceptor implementation.

RequestHandlerInterceptor.java

package com.netsurfingzone.interceptors;

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

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

@Component
public class RequestHandlerInterceptor extends HandlerInterceptorAdapter{
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("preHandle() is invoked");
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("postHandle() is invoked");

	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("afterCompletion() is invoked");
	}

	@Override
	public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("afterConcurrentHandlingStarted() is invoked");
	}
}

WebAppConfigAdapter.java

package com.netsurfingzone.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.netsurfingzone.interceptors.RequestHandlerInterceptor;

@Component
public class WebAppConfigAdapter implements WebMvcConfigurer {

	@Autowired
	RequestHandlerInterceptor interceptor;

	@Override
	public void addInterceptors(InterceptorRegistry interceptorRegistry) {
		System.out.println("this method will get invoked by container while deployment");
		System.out.println("value of interceptor is " + interceptor);
		// adding custom interceptor
		interceptorRegistry.addInterceptor(interceptor);
	}
}

We are done now. Let’s run SpringMainExample.java

How to define custom Spring Boot interceptor.

We can see the addInterceptors() method has been called while deployment.

Let’s hit below URI from the postman.

http://localhost:9092/book/getbook

Spring Boot interceptor example

Observethe console. Now preHandle(), postHandle() and afterCompletion() will get called.


step by step Spring Boot interceptor example

Did you notice! The afterConcurrentHandlingStarted() method has not been called?  This method is used mainly for asynchronous processing. HandlerInterceptorAdapter class further implements AsyncHandlerInterceptor interface, this interface contains afterConcurrentHandlingStarted() method. We have created a new thread using callable in the controller.

	@RequestMapping(value = "/getasync", method = RequestMethod.GET)
	public Callable<Book> handleTestRequest() {
		Callable<Book> callable = new Callable<Book>() {
			public Book call() throws Exception {
				Thread.sleep(100);
				Book book = new Book();
				book.setBookId(1);
				book.setBookName("rich dad poor dad");
				book.setBookPrice("10");
				return book;
			}
		};
		return callable;
	}

Let’s clear the console and hit the below URL from the postman.

localhost:9091/book/getasync

Spring Boot Interceptor example to Modify Request Body

Inside preHandle() method We can set the attribute name, value, and scope in request attributes. Further, this request attributes would be set to RequestContextHolder. The benefit is, we can access these data anywhere in whole application using RequestContextHolder.getRequestAttributes().

Observe the below code snippet.

@Component
public class RequestHandlerInterceptor extends HandlerInterceptorAdapter {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		requestAttributes.setAttribute("some_name", "some_value", RequestAttributes.SCOPE_REQUEST);
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// do-some stuff
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// do-some stuff
	}
}

Now we are going to access request attributes value in our controller class. Let’s modify our controller class as below.

@RestController
@RequestMapping("/book")
public class BookController {

	@RequestMapping(value = "/getbook", method = RequestMethod.GET)
	@ResponseBody
	public Book getBook() {
		Book book = new Book();
		book.setBookId(1);
		book.setBookName("rich dad poor dad");
		book.setBookPrice("10");
		System.out.println("accessing RequestContext Start");
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		System.out.println(requestAttributes.getAttribute("some_name", RequestAttributes.SCOPE_REQUEST));
		System.out.println("accessing RequestContext End");
		return book;
	}
}

Run the application and perform http://localhost:9091/book/getbook call.

In RequestHandlerInterceptor.java we added Request values at line number 7. We are able to access this in the controller. The same way you can access it anywhere in the application ServiceImpl layer or DaoImpl layer.

Spring Boot Logging Interceptor Example

We can also use the interceptors for logging purposes. We can log what request data and response data. Let’s see an example.

package com.netsurfingzone.interceptors;

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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

@Component
public class LoggingInterceptor extends HandlerInterceptorAdapter {

	private static final Logger logger = LogManager.getLogger(LoggingInterceptor.class);

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		requestAttributes.setAttribute("some_name", "some_value", RequestAttributes.SCOPE_REQUEST);
		logger.info("" + request.getRequestURI());
		logger.info("" + request.getMethod());
		// some more information we can get here
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		logger.info("postHanadle() method has been invoked");
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		logger.info("afterCompletion() has been invoked");
		logger.info("Response status is " + response.getStatus());
	}
}

Call http://localhost:9091/book/getbook and observe the logger info.

INFO 11016 — [nio-9091-exec-4] c.n.interceptors.LoggingInterceptor : /book/getbook
INFO 11016 — [nio-9091-exec-4] c.n.interceptors.LoggingInterceptor : GET

INFO 11016 — [nio-9091-exec-4] c.n.interceptors.LoggingInterceptor : postHanadle() method has been invoked
INFO 11016 — [nio-9091-exec-4] c.n.interceptors.LoggingInterceptor :
INFO 11016 — [nio-9091-exec-4] c.n.interceptors.LoggingInterceptor : afterCompletion() has been invoked
INFO 11016 — [nio-9091-exec-4] c.n.interceptors.LoggingInterceptor : Response status is 200

In the above console output, we can see what URL we are passing, what Request Method is there. Also, we can see the Response status code for this request URL. This kind of information we can log using Interceptors.

Spring Boot Authentication Interceptor Example

Spring Interceptors can also be used for security-related stuff. Suppose you want to perform authentication operation then we can have logic inside preHandle() method.

Let’s consider a very simple use case. We have two rest end points.

http://localhost:9091/user/login

http://localhost:9091/user/getuser

For the first Rest API, we don’t want to be secured since anyone can access this API. But while accessing the second Rest API we want to perform some authentication using username and password. In this example, we are not using the database to perform authentication.

@Component
public class AuthenthicationInterceptor extends HandlerInterceptorAdapter {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	    //Some - authentication related code goes here
		return true;
	}
}

Let’s see an example.

Directory structure of exmple.

Define User.java class.

package com.netsurfingzone.entity;

public class User {
	int id;
	String username;
	String email;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

}

Define user define annotation NotSecuredApi.java


package com.netsurfingzone.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface NotSecuredApi {

}

Define AuthenticationInterceptor.java class.

package com.netsurfingzone.interceptors;

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

import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.netsurfingzone.annotation.NotSecuredApi;
import com.netsurfingzone.constants.ApplicationConstant;
import com.netsurfingzone.error.UserNotAuthorizedException;

@Component
public class AuthenticationInterceptor extends HandlerInterceptorAdapter {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		HandlerMethod handlerMethod = null;
		if (handler instanceof HandlerMethod) {
			handlerMethod = (HandlerMethod) handler;
		}

		// Below implementation is for http://localhost:9091/user/login API
		NotSecuredApi notSecuredApi = handlerMethod.getMethodAnnotation(NotSecuredApi.class);
		if (notSecuredApi != null && notSecuredApi instanceof NotSecuredApi) {
			return true;
		}

		// username and password that we are sending from postman
		String username = request.getHeader("username");
		String password = request.getHeader("password");

		// Dummy logic for second API for authentication
		// This should token based authentication so need to send username and
		// password again and again
		if (ApplicationConstant.USERNAME.equals(username) && ApplicationConstant.PASSWORD.equals(password)) {
			return true;
		}

		// if username and password is not admin & admin then show error
		if (!ApplicationConstant.USERNAME.equals(username) || !ApplicationConstant.PASSWORD.equals(password)) {

			throw new UserNotAuthorizedException("Not a valid user");
		}
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// Some more logic if needed
		System.out.println("postHandle() is invoked");

	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// Some more logic if needed
		System.out.println("afterCompletion() is invoked");
	}

}

Define WebAppConfigAdapter.java class where we will register AuthenthicationInterceptor.

package com.netsurfingzone.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.netsurfingzone.interceptors.AuthenthicationInterceptor;

@Component
public class WebAppConfigAdapter implements WebMvcConfigurer {

	@Autowired
	private AuthenticationInterceptor interceptor;

	@Override
	public void addInterceptors(InterceptorRegistry interceptorRegistry) {

		System.out.println("This method will get invoked by container while deployment");
		System.out.println("Value of interceptor is " + interceptor);
		// adding custom interceptor
		interceptorRegistry.addInterceptor(interceptor);
	}
}

Define ApplicationConstant.java class

package com.netsurfingzone.constants;

public class ApplicationConstant {
	public static final String USERNAME = "admin";
	public static final String PASSWORD = "admin";
}

Define User define exception i.e UserNotAuthorizedException.java

package com.netsurfingzone.error;

public class UserNotAuthorizedException extends RuntimeException {

	private static final long serialVersionUID = 1L;
	String message;

	public UserNotAuthorizedException() {
		super();
	}

	public UserNotAuthorizedException(String message) {
		super(message);
		this.message = message;
	}
}

Define ResponseError class.

package com.netsurfingzone.error;

public class ResponseError {
	private String errorMessage;
	private int statusCode;

	public String getErrorMessage() {
		return errorMessage;
	}

	public void setErrorMessage(String errorMessage) {
		this.errorMessage = errorMessage;
	}

	public int getStatusCode() {
		return statusCode;
	}

	public void setStatusCode(int statusCode) {
		this.statusCode = statusCode;
	}

}

Define global error handler class.

package com.netsurfingzone.error;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalErrorHandler {
	@ExceptionHandler(UserNotAuthorizedException.class)
	@ResponseBody
	public ResponseError handleCustomException(UserNotAuthorizedException ex) {
		ResponseError responseError = new ResponseError();
		responseError.setErrorMessage(ex.getMessage());
		responseError.setStatusCode(HttpStatus.UNAUTHORIZED.value());
		return responseError;

	}
}

We are almost done. Let’s define UserController class.


package com.netsurfingzone.controller;

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

import com.netsurfingzone.annotation.NotSecuredApi;
import com.netsurfingzone.constants.ApplicationConstant;
import com.netsurfingzone.entity.User;
import com.netsurfingzone.error.UserNotAuthorizedException;

@RestController
@RequestMapping("/user")
public class UserController {

	@GetMapping("/login")
	@NotSecuredApi
	public String getAuthenticated(@RequestHeader(value = "username") String username,
			@RequestHeader(value = "password") String password) {

		// if username and password is not admin & admin then show error
		if (!ApplicationConstant.USERNAME.equals(username) || !ApplicationConstant.PASSWORD.equals(password)) {

			throw new UserNotAuthorizedException("Not a valid user");
		}
		return "User successfully auntheticated";
	}

	@GetMapping("/getuser")
	public User getUser() {
		User user = new User();
		user.setId(1);
		user.setUsername("Tom");
		user.setEmail("[email protected]");
		return user;
	}

}

SpringMain.java would be same as above example.

Let’s run SpringMain.java class

Testing of Spring Authentication Interceptor Example using postman.

First we will test http://localhost:9091/user/login API and we will test http://localhost:9091/user/getuser API.

We will provide username and password as admin & admin.

Spring Authentication Interceptor Example

Let’s provide some different username than admin.

we are getting the above response since we have implemented global error handling. See more about how to perform global error handling in Spring Boot application.

Let’s test another API.

Let’s try to access http://localhost:9091/user/getuser with different username than admin.

Spring Authentication Interceptor Example

That’s all about Spring Authentication Interceptor Example.

Spring Boot Interceptor example which demonstrates Interceptor will get invoked after Filter.

Both filters and interceptors will get invoked before the request will reach to controller/endpoint classes. In this example, we will see how Filters getting invoked before Interceptors.

We will define a Filter called CustomFilter.java and we have already an interceptor i.e RequestHandlerInterceptor.java. Let’s see the directory structure of Filter.

CustomFilter.java

package com.netsurfingzone.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 org.springframework.stereotype.Component;

@Component
public class CustomFilter implements Filter {

	// this method will be called by container when we send any request
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out
				.println("Hey i am Filter. I will get invoked before that Interceptors. doFilter() method is invoked");
		chain.doFilter(request, response);

	}

	// this method will be called by container while deployment
	@Override
	public void init(FilterConfig config) throws ServletException {
		// do some stuff
	}

	@Override
	public void destroy() {
		// do some stuff like clearing the resources

	}

}

RequestHandlerInterceptor.java

package com.netsurfingzone.interceptors;

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

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

@Component
public class RequestHandlerInterceptor extends HandlerInterceptorAdapter {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println(
				"Hey i am an Interceptor. I will get invoked after that filters. preHandle() method is invoked");
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// Do some important stuff
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// do-some stuff
	}
}

Run SpringMain.java, clear the console, and test using postman.

Spring Boot interceptor example

List of Some important Spring interceptors.

HandlerInterceptor – This is the to-level interceptor contains three methods preHandle(), postHandle() and afterCompletion(). All Spring interceptors(predefined or custom defined) should use this interceptors.

AsyncHandlerInterceptor – This interceptor extends HandlerInterceptor and contains afterConcurrentHandlingStarted() callback method. We have already seen when this method will get invoked.

HandlerInterceptorAdapter – This is an abstract class that implements the AsyncHandlerInterceptor interface.

UserRoleAuthorizationInterceptor – This interceptor extends HandlerInterceptorAdapter and used to check current user roles.

That’s all about the Spring Boot interceptor example.

You may like.

Spring interceptors docs.

Summary – We have seen about Spring Boot interceptor example. We covered how to add interceptors in Spring. We also covered Interceptor will get invoked after Filter.