Reputation: 153
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" }
Upvotes: 15
Views: 50685
Reputation: 357
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.
Upvotes: 4
Reputation: 1522
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);
}
Upvotes: 2
Reputation: 3416
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)
Upvotes: 30