How to send Bearer authorization token using Spring Boot and @FeignClient
Asked Answered
P

3

15

I am using Spring Boot to write an application that interacts with HTTP rest servers. One of the servers I'm connecting to (Wit.ai) uses a beaerer authorization token. A curl request that yields a successful response looks like this:

GET /message?q=sample message HTTP/1.1
Host: api.wit.ai
Authorization: Bearer XXXXXXXXXXXXX
Cache-Control: no-cache
Postman-Token: 526c3a11-8e61-4552-aa19-e913f6473753

The wit.ai docs say the following about the token,

Wit.ai uses OAuth2 as an authorization layer. As such, every API request must contain an Authorize HTTP header with a token Access tokens are app specific.

I am trying to send a GET request to this endpoint in a Spring Boot app using @FeignClient. However I the endpoint doesn't seem to be accepting my authorization token. Here is my FeignClient code

@FeignClient(name="witGetter", url = "${wit.url}")
    public interface WitGetter {
        @RequestMapping(method = RequestMethod.GET, value = "/message?v=20180507q={text}",
            headers = {"Authorization: Bearer XXXXXXXXXXXXX"})
        WitResponse getWitResponse(@PathVariable("text") final String text);
}

What is the proper way to pass such an authorization token? I have tried a few other things but to no avail. Thanks for any advice!!!

By the way, the following code works using a traditional Feign interface, but I need to use @FeignClient in this case.

public interface WitGetter {
    @Headers("Authorization: Bearer XXXXXXXXXXXXX")
    @RequestLine("GET /message?q={text}")
    WitResponse getWitResponse(@Param("text") String text);
}

(code below is in a separate config file)

@Bean
    public WitGetter defaultWitGetter(@Value("https://api.wit.ai") final String witUrl){
        return Feign.builder().decoder(new GsonDecoder()).target(WitGetter.class, witUrl);

}

EDIT

The error code I get when using the above code is:

Exception in thread "main" feign.FeignException: status 400 reading WitGetter#getWitResponse(String,String); content: { "error" : "Bad auth, check token/params", "code" : "no-auth" }

Podite answered 8/5, 2018 at 15:53 Comment(0)
K
30

When using Feign via Spring Cloud, you can use it as you would define a standard Spring MVC controller.

Please check my article here about passing headers with Feign: https://arnoldgalovics.com/passing-headers-with-spring-cloud-feign/

Quick hint: you can add a @RequestHeader("Authorization") String bearerToken parameter to the method definition.

And then of course call it like client.method(..., "Bearer " + token)

Kantar answered 8/5, 2018 at 20:1 Comment(3)
I want to have this bearerToken to be fetched from application properties file. I have them constant for a a Proxy Interface but variable across production & staging environments. What would be the best way to do that? The Feign Proxy Interface wouldn't let me use @Value and I do not want to hard code this token in my codeAdelina
Is there a way to make this posible using filters? Just to do it in one simple filter and not to every request from the feign client..Sparling
@CarlosCruz yes, refer this one: arnoldgalovics.com/…Kantar
W
1
package com.example.demo.web;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.entities.CustomerDto;
import com.example.demo.feign.CustomerRestClient;

import jakarta.servlet.http.HttpServletRequest;

@RestController
@RequestMapping("customers")
@CrossOrigin("*")
public class ClientRestController {
    @Autowired
    private CustomerRestClient customerRestClient;

    @GetMapping("/all")
    public List<CustomerDto> getAllCustomers(HttpServletRequest request) {
        String authorizationHeader = request.getHeader("Authorization");
        return customerRestClient.listCustomes(authorizationHeader);
    }
}

import java.util.List;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;

import com.example.demo.entities.CustomerDto;

import feign.Headers;



@FeignClient(url = "http://localhost:8085/customers",value ="customer-rest-client")
@Headers("Authorization: {token}")
public interface CustomerRestClient {
    @GetMapping("/all")
    public List<CustomerDto> listCustomes(@RequestHeader("Authorization") String token);
}

enter image description here

Waiwaif answered 30/9, 2023 at 1:18 Comment(0)
T
0

You can of course annotate the method with a Header annotation and have an extra token parameter for every call your client provides, but that is not really an elegant solution as the caller needs to have access to the API key.

Actually the easiest and most straight forward solution is to create a configuration that is used by your FeignClient like so:

@Configuration
public class WitGetterConfiguration {

    private final String apiKey;

    public WitGetterConfiguration(@Value("${wit.api-key}") String apiKey) {
        this.apiKey = apiKey;
    }

    @Bean
    public ApiKeyInterceptor interceptor() {
        return new ApiKeyInterceptor(apiKey);
    }
}

The corresponding ApiKeyInterceptor is easy to implement and looks like this:

public class ApiKeyInterceptor implements RequestInterceptor {
    private final String apiKey;

    public ApiKeyInterceptor(String apiKey) {
        this.apiKey = apiKey;
    }

    @Override
    public void apply(RequestTemplate template) {
        template.header("Authorization", "Bearer " + apiKey);
    }
}

Finally you can then set your FeignClient to use the configuration in your client:

@FeignClient(name="witGetter", url = "${wit.url}", configuration = WitGetterConfiguration.class)
public interface WitGetter {
    
    @RequestMapping(method = RequestMethod.GET, value = "/message?v=20180507q={text}")
    WitResponse getWitResponse(@PathVariable("text") final String text);
    
}

This way the API key is sent as part of the header in every request that your client issues.

Tocopherol answered 11/4, 2024 at 16:14 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.