6

I'm trying to implement Feign Clients to get my user info from the user's service, currently I'm requesting with oAuth2RestTemplate, it works. But now I wish to change to Feign, but I'm getting error code 401 probably because it doesn't carry the user tokens, so there is a way to customize, if Spring support for Feign is using, a RestTemplate so I can use my own Bean?

Today I'm implementing in this way

The service the client

@Retryable({RestClientException.class, TimeoutException.class, InterruptedException.class})
@HystrixCommand(fallbackMethod = "getFallback")
public Promise<ResponseEntity<UserProtos.User>> get() {
    logger.debug("Requiring discovery of user");
    Promise<ResponseEntity<UserProtos.User>> promise = Broadcaster.<ResponseEntity<UserProtos.User>>create(reactorEnv, DISPATCHER)
            .observe(Promises::success)
            .observeError(Exception.class, (o, e) -> Promises.error(reactorEnv, ERROR_DISPATCHER, e))
            .filter(entity -> entity.getStatusCode().is2xxSuccessful())
            .next();
    promise.onNext(this.client.getUserInfo());
    return promise;

}

And the client

@FeignClient("account")
public interface UserInfoClient {

    @RequestMapping(value = "/uaa/user",consumes = MediaTypes.PROTOBUF,method = RequestMethod.GET)
    ResponseEntity<UserProtos.User> getUserInfo();
}
Joao Evangelista
  • 2,407
  • 2
  • 26
  • 37

2 Answers2

10

Feign doesn't use a RestTemplate so you'd have to find a different way. If you create a @Bean of type feign.RequestInterceptor it will be applied to all requests, so maybe one of those with an OAuth2RestTemplate in it (just to manage the token acquisition) would be the best option.

Dave Syer
  • 56,583
  • 10
  • 155
  • 143
7

this is my solution, just to complement the another answer with the source code, implementing the interface feign.RequestInterceptor

@Bean
public RequestInterceptor requestTokenBearerInterceptor() {
    return new RequestInterceptor() {
        @Override
        public void apply(RequestTemplate requestTemplate) {
            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
                    SecurityContextHolder.getContext().getAuthentication().getDetails();

            requestTemplate.header("Authorization", "bearer " + details.getTokenValue());
        }
    };
}
Rafael Zeffa
  • 2,334
  • 22
  • 20
  • I used a concrete implementation, so I could avoid null pointers and sending the token to an unprotected resource in case the request does not contain a token or an authenticated user, since for now we can't customize per client interceptor. If you are interested you can check it here: https://github.com/crly/commons/blob/master/src/main/java/io/curly/commons/config/feign/OAuth2FeignRequestInterceptor.java – Joao Evangelista Jul 09 '15 at 17:20
  • good solution!.. in my scenery (one resource server requesting another) a authenticated user/token is always required. thanks for share! – Rafael Zeffa Jul 09 '15 at 18:18
  • Just to add my two cents: It's important to configure the sessionCreationPolicy at least to "IF_REQUIRED" as the SecurityContext will be empty otherwise =) Thanks for this solution, finally got token relay with feign – salgmachine Jul 08 '16 at 10:37
  • Can this be used with Hystrix? – Warren Nocos Sep 06 '16 at 12:09
  • Sure! I'am using with Hystrix here – Rafael Zeffa Sep 06 '16 at 12:27
  • This solution failed with Hystrix, I do not know why your code works. With Hystrix, it faled to get request context. Because isolation strategy of Hystrix is THREAD, the request context cannot be retrieved from Threadlocal. – BurnetZhong Jan 08 '19 at 12:37
  • 1
    for hystrix, you need this `hystrix.shareSecurityContext=true`. – min Jun 04 '19 at 15:43