BoomShaka
BoomShaka

Reputation: 1791

Best way to add a RequestInterceptor to a single FeignClient

I need to add a RequestInterceptor to a specific feign client. The interceptor will add auth information that I do not want to leak to a third party, hence I do not want it to trigger for ALL Feign clients. I have this working, but this seems a tad messy, and am hoping there is a cleaner (less code) option.

I am hoping someone can point to where I can simplify things. Particularly around the encoder/decoder stuff. I really dislike them cluttering up my services constructor like that and find it odd that they even need to be specified in the first place.

I have

// build.gradle
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

I have a RequestInterceptor as

import feign.RequestInterceptor;
import feign.RequestTemplate;

public class BearerAuthRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        // ... SNIP ... (just adds an Authorization header)
    }
}

I have a FeignClient as

@FeignClient(name = "myfeignclient")
public interface MyFeignClient {
    @GetMapping(value = "/doTheThing")
    String doTheThing();
}

I use my FeignClient from a service like so:

@Service
@Import(FeignClientsConfiguration.class)
public class MyService {

  private final MyFeignClient myFeignClient;

  @Autowired
  public MyService(Decoder decoder, Encoder encoder, Contract contract) {
    this.myFeignClient = Feign.builder()
                    .contract(contract)
                    .encoder(encoder)
                    .decoder(decoder)
                    .requestInterceptor(new BearerAuthRequestInterceptor())
                    .target(MyFeignClient.class, "https://whatever.com");
  }

  public void callTheFeignClient() {
      myFeignClient.doTheThing();
  }
}

Upvotes: 1

Views: 12061

Answers (1)

BoomShaka
BoomShaka

Reputation: 1791

Thanks to this comment, I managed to tidy up my implementation a little bit. So no more need for specifying encode/decoder nonsense, or having to manually build my Feign client.

The docs here provide some info, but as is typical they are a bit thin on concrete examples, so perhaps the below will help someone else. Note: I'm using spring boot, and including feign like so in build.gradle implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

First, create the RequestInterceptor like so:

import org.springframework.context.annotation.Bean;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;

import feign.RequestInterceptor;
import lombok.extern.slf4j.Slf4j;

/**
 * A Feign configuration to add the incoming Bearer token to an outgoing feign client request.
 * Only annotate this class with "@Configuration" if you want this interceptor to apply globally to all your Feign clients.
 * Otherwise you risk exposing the auth token to a third party, or adding it unnecessarily to requests that don't need it.
 */
@Slf4j
public class BearerAuthFeignConfig {

    @Bean
    public RequestInterceptor bearerAuthRequestInterceptor() {
        return requestTemplate -> {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication.getPrincipal() instanceof Jwt) {
                Jwt jwt = (Jwt) authentication.getPrincipal();
                requestTemplate.header("Authorization", "Bearer " + jwt.getTokenValue());
            } else {
                log.error("Unable to add Authoriation header to Feign requestTemplate");
            }
        };
    }
}

Then when declaring your feign client, pass the configuration

@FeignClient(
    name = "my-client-that-needs-the-auth",
    configuration = BearerAuthFeignConfig.class,
    url = "http://whatever.com"
)
public interface PlayerManagementClient {
  ...

You'll also need the @EnableFeignClients annotation on your @SpringBootApplication class

Upvotes: 4

Related Questions