I have an API Gateway implemented using Spring Cloud Gateway that uses Spring Security. Spring Security for WebFlux is implemented as a WebFilter right at the beginning of the filter chain. So after successful authentication the request would be forwarded to Spring Cloud Gateway's RoutePredicateHandlerMapping, which would try to deduce the destination based on the URL pattern, and then it would go to a FilteringWebHandler to execute the other filters of Spring Cloud Gateway.
My problem is the following: I have implemented a customized authentication algorithm which uses query string and header variables as credentials for authentication according to the requirements of the project, an this is working without any problem. The problem occurred when we needed to add a small customization for the authentication algorithm that is path independent. When the request reaches the WebFilter of Spring Security, pattern matching is not yet done so I do not know which application does it point to, for example:
Which means that instead of having authentication -> route mapping -> filtering web handler I should do route mapping -> authentication -> filtering web handler. Not that these three components are not similar, one of them is a filter another is a mapper and the last one is web handler. Now I know how to customize them but the problem is that I do not know how to intercept the Netty server building process in order to change the order of these operations. I need to wait for the building process to end and alter the content of the server before it starts. How can I do that?
Why: Because I need to know the Application ID while carrying on my customized authentication process. This Application ID is attached to the request by the RoutePredicateRouteMapping by matching the request's URL to a predefined list.
How did I do it:1- Removing the WebFilter of Spring SecurityI created an HttpHandler bean that invokes the default WebHttpHandlerBuilder and then customize the filters. As a bonus, I removed unneeded filters in order to increase the performance of my API Gateway
2- Wrapping Spring Cloud Gateway's FilteringWebHandler with Spring Web's FilteringWebHandler with the added WebFilterI created my own HandlerAdapter which would match against Spring Cloud Gateway's FilteringWebHandler and wrap it with Spring Web's FilteringWebHandler plus the security filter I extracted in the first step
Spring Security for WebFlux is implemented as a WebFilter which is executed almost as soon as a request is received. I have implemented custom authentication converter and authentication manager which would extract some variables from the header and URL and use them for authentication. This is working without any problem.
Now I needed to add another variable taken from RoutePredicateRouteMapping before authentication is done. What I want exactly is to remove the WebFilter (called WebFilterChainProxy) from its current position and put it between the RoutePredicateRouteMapping and the FilteringWeHandler.
This WebHandler would invoke its filters and then call the DispatchHandler. One of those filters is the WebFilterChainProxy that does the authentication for Spring Security. So first step is removing the filter from here.
Now the DispatchHandler which is called after the filters would invoke RoutePredicateHandlerMapping, which would analyze the routes and give me the route ID that I need, and then it would call the org.springframework.cloud.gateway.handler.FilteringHandler (this is not the same FilteringHandler above), and that in turn would call the other filters of the Spring Cloud Gateway. What I want here is to invoke the filter after RoutePredicatehandlerMapping and before org.springframework.cloud.gateway.handler.FilteringHandler.What I ended doing was the following:
I created and WebHttpHandlerBuilder that would remove WebFilterChainProxy and pass it as a parameter to a customized DispatcherHandler. Now that the filter is removed the request would pass the first layers without requiring authentication. In my customized DispatcherHandler I would invoke the RoutePredicateHandlerMapping and then pass the exchange variable to the WebFilterChainProxy to do the authentication before passing it to the org.springframework.cloud.gateway.handler.FilteringHandler, which worked perfectly!I still think that I'm over engineering it and I hope that there is a way to do it using annotations and configuration beans instead of all these customized classes (WebHttpHandlerBuilder and DispatcherHandler).
You should probably implement that security filter as a proper GatewayFilter, since only those are aware of the other GatewayFilter instances and can be ordered accordingly. In your case, you probably want to order it after the routing one.
I had a similar problem. The accepted solution, while interesting, was a bit drastic for me. I was able to make it work simply by adding my custom filter before SecurityWebFiltersOrder.AUTHENTICATION in the security configuration. This is similar to what I've done with success in a regular Spring mvc application.
You can indeed not define the filter execution order using @WebFilter annotation. However, to minimize the web.xml usage, it's sufficient to annotate all filters with just a filterName so that you don't need the definition, but just a definition in the desired order.
The Servlet 3.0 spec doesn't seem to provide a hint on how a container should order filters that have been declared via annotations. It is clear how about how to order filters via their declaration in the web.xml file, though.
In a web application, we must implement the javax.servlet.Filter interface (jakarta.servlet.Filter in Spring Boot 3) to create filters that can be invoked for either the request to a resource, the response or both. To be more specific, we must write the filter logic in the doFilter() method of the implementation class.
Spring web module provides several inbuilt Filter implementations that we can use for specific purposes. These are abstract classes so we must extend and customize them to use in an application. Some of the most used Spring filters are:
The @WebFilter is part of Servlet 3.0 spec. The @WebFilter annotated classes can be automatically registered with an embedded servlet container. We can use the @WebFilter annotation in place of @Component annotation.
By default, a filter is applied on all URLs in the application. If we want to restrict a filter to certain URLs then we must register the filter and URL pattern with FilterRegistrationBean.
In the following example, we are configuring the SecurityFilter to be invoked only when the URL pattern matches the pattern "/admin/*". We are also setting the precedence or order of filter in the filter chain.
For this demo, we have created all three filters discussed in the tutorial i.e. LoggingFilter, SecurityFilter, AuditFilter and in the same order. Initially, there is no URL filtering so all filters will invoke in the given order.
In this Spring boot tutorial, we learned to define, order and configure the servlet Filter using different ways. We also learned to disable a Filter using FilterRegistrationBean. Finally, we saw a demo of Spring Filters in action.
Custom filters can be created to handle cross-cutting concerns. Examples of cross-cutting concerns include error handling, caching, configuration, authorization, and logging. Filters avoid duplicating code. For example, an error handling exception filter could consolidate error handling.
Implement either the synchronous or the async version of a filter interface, not both. The runtime checks first to see if the filter implements the async interface, and if so, it calls that. If not, it calls the synchronous interface's method(s). If both asynchronous and synchronous interfaces are implemented in one class, only the async method is called. When using abstract classes like ActionFilterAttribute, override only the synchronous methods or the asynchronous methods for each filter type.
When there are multiple filters for a particular stage of the pipeline, scope determines the default order of filter execution. Global filters surround class filters, which in turn surround method filters.
Controller level filters set the Order property to int.MinValue. Controller level filters can not be set to run after filters applied to methods. Order is explained in the next section.
The default sequence of execution can be overridden by implementing IOrderedFilter. IOrderedFilter exposes the Order property that takes precedence over scope to determine the order of execution. A filter with a lower Order value:
In the Controller level filters example, GlobalSampleActionFilter has global scope so it runs before SampleActionFilterAttribute, which has controller scope. To make SampleActionFilterAttribute run first, set its order to int.MinValue:
The filter pipeline can be short-circuited by setting the Result property on the ResourceExecutingContext parameter provided to the filter method. For example, the following Resource filter prevents the rest of the pipeline from executing:
Therefore the ResponseHeaderAttribute filter never runs for the Index action. This behavior would be the same if both filters were applied at the action method level, provided the ShortCircuitingResourceFilterAttribute ran first. The ShortCircuitingResourceFilterAttribute runs first because of its filter type:
Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). Constructor dependencies cannot be provided by DI because attributes must have their constructor parameters supplied where they're applied.
Loggers are available from DI. However, avoid creating and using filters purely for logging purposes. The built-in framework logging typically provides what's needed for logging. Logging added to filters:
ff7609af8f