It's my first time at Spring Cloud Gateway implementation.
I need filter every request and apply a filter validation on some paths. Following the Baeldung Custom Filters tutorial I make a simple application to filter requests.
The application must release paths like /actuator/health
and validate specific paths to backend service. So far, I've implemented a GlobalFilter
and a GatewayFilterFactory
. The Global filter is called every request but the GatewayFilter is called just once when application starts, that way I can't make the auth logic to every request. The auth logic is about a specific header field. So, my grained questions are:
GlobalFilter
@Component
public class LoggingGlobalPreFilter implements GlobalFilter {
final Logger LOGGER = LoggerFactory.getLogger(LoggingGlobalPreFilter.class);
@Override
public Mono<Void> filter(
ServerWebExchange exchange,
GatewayFilterChain chain) {
LOGGER.info("Global Pre Filter executed");
return chain.filter(exchange);
}
}
GatewayFilter
@Component
public class LoggingGatewayFilterFactory extends
AbstractGatewayFilterFactory<LoggingGatewayFilterFactory.Config> {
final Logger LOGGER =
LoggerFactory.getLogger(LoggingGatewayFilterFactory.class);
public LoggingGatewayFilterFactory() {
super(Config.class);
}
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
return response.setComplete();
}
private boolean isAuthorizationValid(String authorizationHeader) {
boolean isValid = true;
return authorizationHeader.equals("x-header");
}
@Override
public GatewayFilter apply(Config config) {
LOGGER.info("M=apply, Msg=Applying Gateway Filter....");
return ((exchange, chain) -> {
LOGGER.info("M=apply, Msg=Applying Gateway Filter...."); // APARENTELLY NEVER ENTER HERE.
ServerHttpRequest request = exchange.getRequest();
if (!request.getHeaders().containsKey(TsApiGatewayConstants.HEADER_APIKEY)) {
return this.onError(exchange, TsApiGatewayConstants.MESSAGE_API_KEY_MISSING, HttpStatus.UNAUTHORIZED);
}
String apiKey = request.getHeaders().get(TsApiGatewayConstants.HEADER_APIKEY).get(0);
String userAgent = request.getHeaders().get(TsApiGatewayConstants.HEADER_USER_AGENT).get(0);
if (!this.isAuthorizationValid(userAgent)) {
return this.onError(exchange, TsApiGatewayConstants.MESSAGE_API_KEY_INVALID, HttpStatus.UNAUTHORIZED);
}
return chain.filter(exchange);
});
}
public static class Config {
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
public Config(String baseMessage, boolean preLogger, boolean postLogger) {
this.baseMessage = baseMessage;
this.preLogger = preLogger;
this.postLogger = postLogger;
}
public String getBaseMessage() {
return baseMessage;
}
public void setBaseMessage(String baseMessage) {
this.baseMessage = baseMessage;
}
public boolean isPreLogger() {
return preLogger;
}
public void setPreLogger(boolean preLogger) {
this.preLogger = preLogger;
}
public boolean isPostLogger() {
return postLogger;
}
public void setPostLogger(boolean postLogger) {
this.postLogger = postLogger;
}
}
}
application.yml
cloud:
gateway:
routes:
- id: service_route
uri: https://backend-url:443
predicates:
- Path=/api
filters:
- Logging
Example path to filter: https://backend-url:443/api/service1
Implementing Spring Cloud Gateway Filters using Property based Configuration. Run the application. Go to localhost:8080/employee/message- In the console we can see that pre filter has been applied to request. In the browser we can see that the post filter has been applied to response.
There are three ways to add your filter, Annotate your filter with one of the Spring stereotypes such as @Component. Register a @Bean with Filter type in Spring @Configuration. Register a @Bean with FilterRegistrationBean type in Spring @Configuration.
I've found a way to solve it. I've used a RouteConfiguration component to set the routes and a GatewayFilter class. On the RouteConfiguration's Bean I've seted the specific filter to the route path. On my case I've used a filter to make an authentication.
GatewayFilter
@RefreshScope
@Component
public class AuthenticationFilter implements GatewayFilter {
final Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// Make your business logic, this is a simple sample.
if (!request.getHeaders().containsKey("x-api-key")) {
return this.onError(exchange,"api-key missing",HttpStatus.FORBIDDEN);
}
return chain.filter(exchange); // Forward to route
}
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(httpStatus);
return response.setComplete();
}
RouteConfiguration
@RefreshScope
@Configuration
public class RouteConfiguration {
@Value("${routes.api}")
private String apiHost;
@Autowired
AuthenticationFilter authenticationFilter;
@Bean
public RouteLocator apiRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route("CHOICE A ROUTE ID",p -> p
.path("/api/**")
.filters(f -> f
.filter(authenticationFilter) // You can add more filters here.
.stripPrefix(1))
.uri(apiHost))
.build();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With