Reputation: 1791
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
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