3

I'm using feign client to call other services. Service A need to contact Service B and has to be authenticated via authentication service.

If I wasn't using feign, I would just use resttemplate calling first the authentication service.. get the token, add it to the header of the msg I want to send to service B.

Is it possible to configure to feign an endpoint that from there he gets the token so it would be done automatically?

nadavgam
  • 2,014
  • 5
  • 20
  • 48

1 Answers1

0

Considering you have prepeared JWT authroization and authentication in the authentication service, i can give my example like this:

First feign client using OpenFeign (the latest feign implementation of spring)

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

// Must give a url to NOT use load balancer (we have a gateway)
// Otherwise it will throw "did you forget load balancer?" error
@FeignClient(url = "http://localhost:<port>/", name = "UserClient")
public interface UserClient
{
    @PostMapping(path = "login", consumes = "application/json")
    ResponseEntity<String> login(@RequestBody String user);
    
    @GetMapping(path = "hello")
    ResponseEntity<String> sayHello();
}

Then get the jwt token like this: (This code can be in some mvc project)

String username = someuser
String password = somepassword
// I wasn't able to create a json but the below data would work with spring security
String user = "{\"username\":\"" + username + "\",\"password\":\"" + password + "\"}";
ResponseEntity<String> responseEntity = userClient.login(user);
// your token could be like "<username> - <token>" kind of structure
String token = responseEntity.getBody();

Then make an authenticated request with openfeign, this code can be in mvc project or something

// If you put the token in session
String bearer = (String) req.getSession().getAttribute("Bearer");
UserClient helloClient = Feign.builder().client(new Client.Default((SSLSocketFactory) SSLSocketFactory.getDefault(), null)).contract(new SpringMvcContract()).requestInterceptor(new RequestInterceptor()
{
    @Override
    public void apply(RequestTemplate template)
    {
        System.err.println("This is adding jwt header to resttemplate of the feign");
        template.header("Authorization", "Bearer " + bearer);
    }
}).decoder(new Decoder()
{
    @Override
    public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException
    {
        // If you have returned responseentity, feign client must be able to understand it
        return new ResponseEntity<>(response.body().toString(), HttpStatus.resolve(response.status()));
    }
}).target(UserClient.class, "http://localhost:4441/hello/");
// My responsebody is a simple string but it could be anything you want, as long as you have the decoder
String response = helloClient.sayHello().getBody();

Feign clients does not have the ability to dynamically set headers, especially from jwt tokens because those tokens are coming from either another request or in session. That is why i use builder to set a dynamic header. If you use BasicAuthRequestInterceptor, you can create a @Bean of BasicAuthRequestInterceptor and set a static username and password inside it.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Numan Karaaslan
  • 1,365
  • 1
  • 17
  • 25