Tim Biegeleisen
Tim Biegeleisen

Reputation: 520878

Spring Cloud Gateway pass bean to custom filter

We are attempting to use Spring Cloud Gateway to setup a microservice based architecture. Currently, we have defined a route programatically:

@ServletComponentScan
@SpringBootApplication
public class GatewayApplication {
    // to be passed to and used by custom filter
    @Autowired
    RestTemplate restTemplate;

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
        .route("status", r -> r
            .method(HttpMethod.GET)
            .and()
            .path("/status")
            .filters(f -> f.rewritePath("/status", "/v2/status")
                           .filter(new AuthorizationFilter(restTemplate).apply(new Config(""))))
            .uri("http://localhost:8081/"))
        .build();
    }
}

The above would route an incoming request /status via GET to another endpoint. We would like to apply a custom filter, which we have implemented in AuthorizationFilter. This filter, as the name implies, is another microservice which will either allow or deny an incoming request based on credentials and permissions.

Currently, the pattern we are following, which works, is to inject a Spring RestTemplate into the gateway class above, and then to pass this RestTemplate to the constructor of the filter.

However, how can this be done if we wanted to switch to using a YAML file for defining all the routes? Presumably in both cases Spring would be constructing a new filter for each incoming request. But in the case of YAML, how can we pass something in the construtor? If this cannot be done, is there any other way to inject a RestTemplate, or any other resource into a custom Spring gateway filter?

Upvotes: 2

Views: 4439

Answers (1)

Dimitri Mestdagh
Dimitri Mestdagh

Reputation: 44665

You can register your own custom GatewayFilterFactory. This allows you to provide a custom configuration, and within that configuration, you can use SpEL to reference a bean.

For example:

@Component
public class AuthenticationGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthenticationGatewayFilterFactory.Config> {
    public AuthenticationGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // TODO: Implement
    }

    public static class Config {
        private RestTemplate restTemplate;

        // TODO: Getters + Setters
    }
}

Now you can use SpEL to properly reference a RestTemplate bean:

spring:
  cloud:
    gateway:
      routes:
        - id: status
          uri: http://localhost:8081/
          filters:
            - name: Authentication
              args:
                restTemplate: "#{@nameOfRestTemplateBean}"
          predicates:
            - Path=/status

Alternatively, you could inject a RestTemplate bean within your gateway filter. For example:

@Component
public class AuthenticationGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthenticationGatewayFilterFactory.Config> {
    private RestTemplate restTemplate;

    public AuthenticationGatewayFilterFactory(RestTemplate restTemplate) {
        super(Config.class);
        this.restTemplate = restTemplate;
    }

    @Override
    public GatewayFilter apply(Config config) {
        // TODO: Implement
    }

    public static class Config {
        // TODO: Implement
    }
}

The code/configuration necessary to do the inject is less complex, but it also makes it more difficult if you ever decide to put AuthenticationGatewayFilterFactory in a separate library, as the "consumers" of this library won't have any control over which RestTemplate is being injected.

Upvotes: 3

Related Questions