A Deep Dive Into Spring Interceptors: A Comprehensive Guide
In the world of Spring Boot, developers often encounter the need to perform specific actions either before or after processing HTTP requests. This is where Spring Interceptors and Servlet Filters come into play.
1. Introduction to Spring Interceptors
Spring Interceptors are components that are configured to intercept incoming HTTP requests before they reach their designated controllers. They serve a crucial role in managing cross-cutting concerns, such as logging, security, data transformation, and more.
Here are a few real-world use-cases of Spring Interceptors:
Logging: You can use an interceptor to log the details of all incoming requests to the application and outgoing responses from the application.
Performance Monitoring: Interceptors can calculate the time taken to process requests by recording the time before and after the request processing.
Authentication and Authorization: Interceptors can be used to verify whether an incoming request has the necessary credentials and permissions to access specific resources.
2. Spring Interceptors vs Servlet Filters
While both Spring Interceptors and Servlet Filters can intercept requests, there are some notable differences between them:
Scope of Application: Servlet Filters are applied at the Web Container level before the request reaches the Dispatcher Servlet, while Spring Interceptors apply after the request is processed by the Dispatcher Servlet but before it reaches the Controller.
Execution Order: Servlet Filters are executed before Spring Interceptors. They can modify the request and response objects while Spring Interceptors have less control over these.
Access to Spring Beans: Servlet Filters don't have access to Spring Beans as they operate outside of the Spring context, whereas Spring Interceptors do, as they are fully integrated into the Spring lifecycle.
3. Implementing and Registering a Spring Interceptor
To implement a Spring Interceptor, you'll need to create a class that implements the HandlerInterceptor
interface, then override its methods (preHandle()
, postHandle()
, afterCompletion()
), and finally, register the interceptor in your Spring configuration.
@Component
public class CustomInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// code to be executed before the request is handled by the controller
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
// code to be executed after the request is handled by the controller, and the view is about to be rendered
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// code to be executed after the request has been fully processed
}
}
To register this interceptor, you'll need to define an @Configuration
class that implements WebMvcConfigurer
, and override the addInterceptors()
method.
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Autowired
private CustomInterceptor customInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor);
}
}
4. Interceptor Execution Order
When there are multiple interceptors, they are executed in the order they were added in the addInterceptors()
method. The preHandle()
methods of each interceptor will execute in the given order, but postHandle()
and afterCompletion()
methods execute in reverse order.
If you want to specify a custom order, you can implement the Ordered
interface in your interceptor classes and override the getOrder()
method to return an integer that specifies the order.
5. Building an Interceptor for Basic Authentication Checks
Now, let's build a Basic Authentication interceptor. Basic Authentication uses Base64 encoded 'username:password' pairs for authentication.
@Component
public class BasicAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null) {
String[] authParts = authHeader.split(" ");
String authInfo = authParts[1];
byte[] bytes = null;
bytes = Base64.getDecoder().decode(authInfo);
String decodedAuth = new String(bytes);
String[] credentials = decodedAuth.split(":");
String username = credentials[0];
String password = credentials[1];
// Replace with actual authentication logic
if ("user".equals(username) && "pass".equals(password)) {
return true;
}
}
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
}
This interceptor extracts the Authorization
header, decodes the Base64 encoded 'username:password' pair, and verifies the credentials. If the credentials are invalid or missing, it responds with a 401 Unauthorized status.
Remember to add this interceptor to the addInterceptors()
method in your configuration class.
And there you have it! Now you're well-equipped to use Spring Interceptors in your projects.
They offer an excellent way to handle cross-cutting concerns in a clean, modular manner.