Brian
Brian

Reputation: 7654

Multiple Feign clients with different interceptors

I have two Feign clients in Spring Boot doing different things, but would like them to be authenticated differently.

@FeignClient(
    name = "...",
    url = "${url1}",
    configuration = Config1.class
)
public interface Client1 {
    @PostMapping(
        path = "...",
        consumes = MediaType.APPLICATION_JSON_VALUE,
        produces = MediaType.APPLICATION_JSON_VALUE)
    JsonNode doThing(@RequestBody JsonNode thing);
}

@FeignClient(
    name = "...",
    url = "${url2}",
    configuration = Config2.class
)
public interface Client2 {
    @PostMapping(
        path = "...",
        consumes = MediaType.APPLICATION_JSON_VALUE,
        produces = MediaType.APPLICATION_JSON_VALUE)
    JsonNode doThing(@RequestBody JsonNode thing);
}

They both need basic authentication, but different values for username and password. For that, I thought about having separate Config classes to set their respective clients:

@Configuration
public class Client1 {
    private final String user;
    private final String password;

    public Client1(final Config1 config) {
        this.user = config.getUser();
        this.password = config.getPassword();
    }

    @Bean(name = "client1")
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor(user, password);
    }
}

@Configuration
public class Client2 {
    private final String user;
    private final String password;

    public Client1(final Config2 config) {
        this.user = config.getUser();
        this.password = config.getPassword();
    }

    @Bean(name = "client2")
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor(user, password);
    }
}

But my API is returning HTTP 4xx errors, as if the interceptor did not work at all. Can I get some pointers on setting this up properly?

(Notice that I gave those beans names, because they will otherwise conflict for DI.)

Upvotes: 5

Views: 6782

Answers (3)

Marius Manastireanu
Marius Manastireanu

Reputation: 2576

I know this question is old, but it seems that it was lacking an actual working response.

I managed to achieve this doing the following:

  1. Define two Configurators for the Clients
@Configuration
public class ClientAConfig {

    @Bean(name = "clientARequestInterceptor")
    public RequestInterceptor clientARequestInterceptor() {
        return requestTemplate -> {
            requestTemplate.header("Authorization", "Bearer tokenA");
        };
    }

}

and a similar one with different content for ClientBConfig

  1. Define FeignClients as such:
@FeignClient(name = "ClientAClient", url = "...", configuration = ClientAConfig.class)
public interface ClientAClient {...}
@FeignClient(name = "ClientBClient", url = "...", configuration = ClientBConfig.class)
public interface ClientBClient {...}
  1. Provide custom Feign configurator that lets the know FeignClient not to inherit beans from the parent context.
@Configuration
public class FeignConfiguration {

    @Bean
    public FeignClientConfigurer feignClientConfigurer() {
        return new FeignClientConfigurer() {

            @Override
            public boolean inheritParentConfiguration() {
                return false;
            }

        };
    }
}

source: https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html#:~:text=If%20we%20want%20to%20create,collision%20of%20these%20configuration%20beans.

Upvotes: 2

fotg
fotg

Reputation: 21

Try remove @Configuration annotation, and use ApplicationContext to read configed properties:

// config
import feign.RequestInterceptor;
import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.context.annotation.Bean;
public class Client1 {

    @Bean(name = "amapRequestInterceptor")
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        String user = SpringUtils.getProperty("client1.user", String.class, null);
        String password = SpringUtils.getProperty("client1.password", String.class, null);
        return new BasicAuthRequestInterceptor(user, password);
    }
}
// SpringUtils
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

@Component
public class SpringUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        if (SpringUtils.applicationContext == null) {
            SpringUtils.applicationContext = applicationContext;
        }
    }

    public static <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
        return applicationContext.getEnvironment().getProperty(key, targetType, defaultValue);
    }

}

The problem in your code is feign will use all of RequestInterceptor in application context so that Authorization header is overwrite twice.

With @Configuration removed, there is no RequestInterceptor in application context, feign will use RequestInterceptor specified by @FeignClient.

Upvotes: 0

MichaPoe
MichaPoe

Reputation: 126

I guess You have to remove the stereotype @Configuration.

I actually came here during a search to a similar problem. I do have (same like You) two different configs. One FeignClient with and a second client without auth. But the second client is using both RequestInterceptors (I implemented a noop-RequestInterceptor just for logging).

Could You actually solve your problem?

Upvotes: 4

Related Questions