How to add a request interceptor to a feign client?
Asked Answered
B

3

25

I want every time when I make a request through feign client, to set a specific header with my authenticated user.

This is my filter from which I get the authentication and set it to the spring security context:

@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
public class PerformanceApplication {
    @Bean
    public Filter requestDetailsFilter() {
        return new RequestDetailsFilter();
    }

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

    private class RequestDetailsFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {

        }

        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            String userName = ((HttpServletRequest)servletRequest).getHeader("Z-User-Details");
            String pass = ((HttpServletRequest)servletRequest).getHeader("X-User-Details");
            if (pass != null)
                pass = decrypt(pass);
            SecurityContext secure = new SecurityContextImpl();
            org.springframework.security.core.Authentication token = new UsernamePasswordAuthenticationToken(userName, pass);
            secure. setAuthentication(token);
            SecurityContextHolder.setContext(secure);
            filterChain.doFilter(servletRequest, servletResponse);
        }

        @Override
        public void destroy() {

        }
    }
    private String decrypt(String str) {
        try {
            Cipher dcipher = new NullCipher();

            // Decode base64 to get bytes
            byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);

            // Decrypt
            byte[] utf8 = dcipher.doFinal(dec);

            // Decode using utf-8
            return new String(utf8, "UTF8");
        } catch (javax.crypto.BadPaddingException e) {
        } catch (IllegalBlockSizeException e) {
        } catch (UnsupportedEncodingException e) {
        } catch (java.io.IOException e) {
        }
        return null;
    }
}

This is my feign client:

@FeignClient("holiday-client")
public interface EmailClient {
    @RequestMapping(value = "/api/email/send", method = RequestMethod.POST)
    void sendEmail(@RequestBody Email email);
}

And here I have a request interceptor:

@Component
public class FeignRequestInterceptor implements RequestInterceptor {
    private String headerValue;

    public FeignRequestInterceptor() {
    }

    public FeignRequestInterceptor(String username, String password) {
        this(username, password, ISO_8859_1);
    }

    public FeignRequestInterceptor(String username, String password, Charset charset) {
        checkNotNull(username, "username");
        checkNotNull(password, "password");
        this.headerValue = "Basic " + base64encode((username + ":" + password).getBytes(charset));
    }

    private static String base64encode(byte[] bytes) {
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(bytes);
    }

    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("Authorization", headerValue);
    }
}

I don't know how to configure this interceptor to my client and how to set the header with the username and password. How can I accomplish that ?

Bannerman answered 26/10, 2016 at 12:19 Comment(0)
A
23

You don't really need your own implementation of the FeignRequestInterceptor as there is already BasicAuthRequestInterceptor in the feign.auth package that does exactly the same.

That being said, you basically have almost everything set up already. All is left to do is to define the basicAuthRequestInterceptor bean with specific username and password:

@Bean
public RequestInterceptor basicAuthRequestInterceptor() {
    return new BasicAuthRequestInterceptor("username", "password");
}
Acetylene answered 28/3, 2017 at 14:26 Comment(2)
Is there possible to add @Qualifier for RequestInterceptor @Bean?Painstaking
Sure it's possible. But Feign is not aware of custom qualifiers. If you need to switch between RequestInterceptors you need to introduce some custom Feign Configuration.Acetylene
B
8

I know the thread is a bit old but wanted to give some explanation on what's happening here.

If you'd like to customize your Feign requests, you can use a RequestInterceptor. This can be a custom implementation or you can reuse what's available in the Feign library, e.g. BasicAuthRequestInterceptor.

How to register it? Well, there 2 ways to do it depending on how you use Feign.

If you're using plain Feign without Spring, then you gotta set the interceptor to the Feign builder. An example is here.

Feign.builder()
     .requestInterceptor(new MyCustomInterceptor())
     .target(MyClient.class, "http://localhost:8081");

If you're using Spring Cloud OpenFeign and you use the @FeignClient annotation to construct your clients, then you have to create a bean from your RequestInterceptor by either defining it as a @Component or as a @Bean in one of your @Configuration classes. Example here.

@Component
public class MyCustomInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        // do something
    }
}

Also, you can check out one of my articles in this topic, maybe that clears it up better: Customizing each request with Spring Cloud Feign

Bonn answered 4/12, 2021 at 16:20 Comment(3)
I'm using Spring Cloud OpenFeign, with the @FeignClient annotations. Is there a way to only add the request interceptor to a specific feign client, or is the only way to do that to use Feign.builder().requestInterceptor ?Vickeyvicki
@Vickeyvicki The @FeignClient annotation gives you a configuration attribute where you can specify which configuration class you want to be picked up for that particular feign client. In your case, create a configuration class with the request interceptor defined as a Spring Bean and apply that configuration in the @FeignClient annotation where you need it.Bonn
Awesome, good job. Cheers mate.Bonn
P
1

This is straight forward. No need of any further annotations. Just copy paste, modify and use :)

@Configuration
public class ClientConfig {

    private static final String BEARER = "Bearer ";
    private static final String AUTHORIZATION_HEADER = "Authorization";

    @Bean
    public RequestInterceptor getRequestInterceptor(JwtTokenService jwtTokenService) {
        return requestTemplate -> requestTemplate
                .header(AUTHORIZATION_HEADER, BEARER + jwtTokenService.generateSignedJwt());
    }
}
Presentiment answered 23/4, 2024 at 12:31 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.