Contents
What are spring Filters?
A Filter is a plain object that can be used to intercept the HTTP requests and responses coming in to and going out from your application. We can use these filter objects to perform pre-processing before HTTP requests reach the controllers. Also we can perform post processing after HTTP requests are sent out by the controllers.
What is a security filter or filter chain?
Spring Security's web infrastructure is based on Servlet filters. There are many filters in the web infrastructure each is responsible for a particular task. These filters are connected by placing one after another by ordering them among themselves. Filters can be added or removed from the security configuration. Developers can write their own filters and place it in a specific position in the filter chain by specifying the order of the filter in the filter chain. The order is important as one filter can depend on another. Red more about the Spring Filter Chain in spring documentation.
What is FilterRegistrationBean ?
FilterRegistrationBean Is a Spring Bean that helps to register a Custom filter to the spring container. A Spring Bean is an object that can be created once and registered to the spring container so that we can use this bean by calling its name in other Java classes of the application. Here we will not use the Filter from anywhere in the application but let Spring know that we want to introduce a new filter in the filter chain in a certain position in the filter chain.
How to add a custom filter in the Spring Security Filter Chain?
Here I used the FilterRegistrationBean to create a new filter in the Spring security chain. This filter performs an extra check in the spring boot security chain.
We have the spring security configuration in the application to protect REST endpoints. You can create your security configuration, refer to the spring documentation on SecurityConfig.
Sample code to register my own security check in filter chain
import javax.inject.Inject;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MySecurityConfig {
@Inject
MySpecialFilter mySpecialFilter;
@Bean
public FilterRegistrationBean filter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(mySpecialFilter);
filterRegistrationBean.addUrlPatterns("/api/v1/*")
return filterRegistrationBean;
}
}
How to write a custom filter for Spring Security Filter Chain?
You need to implement the java Filter interface in your custom filter and override doFilter(), init() and destroy() methods. You have to add your custom security logic in the doFIlter() method, you can access request and response objects. Any logic written before chain.dofilter() it will be executed when the turn of your filter comes in the chain and before passing control to the next element in the filter chain at the time of request processing is done in the application. Any logic written after chain.dofilter() it will be executed when response is being sent out of the application and this filter is executed in the filter chain.
Sample code to Custom Filter for Spring security Filter chain
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.code.Response.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
@Component
public class MySpecialFilter implements Filter {
@Override
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, {
HttpServletRequest req = ( HttpServletRequest ) request;
HttpServletResponse res = ( HttpServletResponse ) response;
try{
if(check(res.getPathInfo())) {
chain.doFilter(request, response);
} else {
logger.error("print something"+ e.getMessage(), e);
res.sendError(Status.FORBIDDEN.getStatusCode(), "user not authenticated");
}
} catch(Exception e) {
logger.error("print something"+ e.getMessage(), e);
res.sendError(Status.FORBIDDEN.getStatusCode(), "user not authenticated");
}
}
@VisibleForTesting //can use when the method is package private(means default)
boolean check(String path) {
//return true or false
}
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void destroy() { }
}
res.sendError(int, String) will work in spring boot 1.5.x and spring 4.1.11
But in spring boot 2.1.x and spring 5.1.12 it will not work, spring filter chain will not pass your message and error code and only show status 401 with no error body. This is a feature of spring filter chain in spring 5 that , when a request fails to pass security filter chain spring only returns 401. To be able to send your own error code and error message we need to replace response.sendError() by :
res.setStatus(403);
res.getWriter().write(”your custom error message”)
If we would like to send a json as body we can send the json object in the write() method.
Where my custom filter is placed in Spring’s Security Filter Chain?
Spring Security does not set an order on the Filter bean that it creates. When Boot is creating a FilterRegistrationBean for it, it gets the default order which is LOWEST_PRECEDENCE. In my example I want my filter to process requests at the end of the security filter chain, that is why I have not specified any order.
How to change the default order of the custom filter in the Spring’s Security Filter Chain?
You need to specify it on your own, If you want your custom filter to be placed in a specific position in the spring’s security filter chain. You have to create your own registration for Spring Security's filter as I have shown above and specify the order. As an example
You can specify a relative value
filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE - 1);
You also can specify an absolute value
filterRegistrationBean.setOrder(0);
The numeric value of the HIGHEST_PRECEDENCE and LOWEST_PRECEDENCE changes when spring version changes, so before using the absolute value you need to verify it.
Or you can use the @Order directive to order your Filter for example you can annotate the filter with highest precedence so that it executes first in the filter chain (@Order(Ordered.HIGHEST_PRECEDENCE))
Yorumlar