16

I have a microservice architecture, both of them securized by spring security an JWT tokens.

So, when I call my first microservice, I want to take the JWT token and send a request to another service using those credentials.

How can I retrieve the token and sent again to the other service?

dragonalvaro
  • 703
  • 1
  • 10
  • 28

4 Answers4

16

Basically your token should be located in the header of the request, like for example: Authorization: Bearer . For getting it you can retrieve any header value by @RequestHeader() in your controller:

@GetMapping("/someMapping")
public String someMethod(@RequestHeader("Authorization") String token) {

}

Now you can place the token within the header for the following request:

HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);

HttpEntity<RestRequest> entityReq = new HttpEntity<RestRequest>(request, headers);

Now you can pass the HttpEntity to your rest template:

template.exchange("RestSvcUrl", HttpMethod.POST, entityReq, SomeResponse.class);

Hope I could help

paddy_89
  • 298
  • 2
  • 5
  • exactly. same way you'd do with any header. – eis Oct 14 '17 at 16:14
  • seems pretty solid solution, but it's a little "hand craft". I was looking to use a different solution using a interceptor like ClientHttpRequestInterceptor. But I don't know how to retrieve the token from there. For example, the sleuth library with TraceInterceptorConfiguration is doing the same. Don't need to manually add headers – dragonalvaro Oct 16 '17 at 10:08
  • Or you can use an controller advice (Class level annotation) instead of an interceptor. There you also have the possibility to inject headers – paddy_89 Oct 17 '17 at 11:39
11

I've accomplished the task, creating a custom Filter

public class RequestFilter implements Filter{



    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader(RequestContext.REQUEST_HEADER_NAME);

        if (token == null || "".equals(token)) {
            throw new IllegalArgumentException("Can't retrieve JWT Token");
        }

        RequestContext.getContext().setToken(token);
        chain.doFilter(request, response);

    }

    @Override
    public void destroy() { }

    @Override
    public void init(FilterConfig arg0) throws ServletException {}


}

Then, setting in my config

    @Bean
public FilterRegistrationBean getPeticionFilter() {

    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new RequestFilter());
    registration.addUrlPatterns("/*");
    registration.setName("requestFilter");

    return registration;
}

With that in mind, I've create another class with a ThreadLocal variable to pass the JWT token from the Controller to the Rest Templace interceptor

public class RequestContext {

public static final String REQUEST_HEADER_NAME = "Authorization";

private static final ThreadLocal<RequestContext> CONTEXT = new ThreadLocal<>();

private String token;

public static RequestContext getContext() {
    RequestContext result = CONTEXT.get();

    if (result == null) {
        result = new RequestContext();
        CONTEXT.set(result);
    }

    return result;
}

public String getToken() {
    return token;
}

public void setToken(String token) {
    this.token = token;
}

}

public class RestTemplateInterceptor implements ClientHttpRequestInterceptor{

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

    String token = RequestContext.getContext().getToken();

    request.getHeaders().add(RequestContext.REQUEST_HEADER_NAME, token);

    return execution.execute(request, body);

}

}

Add interceptor to the config

  @PostConstruct
public void addInterceptors() {
    List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
    interceptors.add(new RestTemplateInterceptor());
    restTemplate.setInterceptors(interceptors);
}
dragonalvaro
  • 703
  • 1
  • 10
  • 28
  • Hi man, I get the same problem can you show all the code. My resftul api works fine but I have problems with the client side. – Krismorte Sep 12 '18 at 18:15
  • @dragonalvaro which RequestContext you have used ? – Bhargav Patel Aug 14 '19 at 05:23
  • https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/support/RequestContext.html – dragonalvaro Aug 26 '19 at 06:22
  • This looks dangerous because `CONTEXT.remove()` is never called. Web servers usually use thread pools, and if you set a value to threadlocal and don't remove it, it will available in other requests. https://stackoverflow.com/questions/30328437/will-the-threadlocal-object-be-cleared-after-thread-returned-to-thread-pool – Yury Kisliak Feb 14 '20 at 13:47
3

I think it is better to add the interceptor specifically to the RestTemplate, like this:

class RestTemplateHeaderModifierInterceptor(private val authenticationService: IAuthenticationService) : ClientHttpRequestInterceptor {
    override fun intercept(request: org.springframework.http.HttpRequest, body: ByteArray, execution: ClientHttpRequestExecution): ClientHttpResponse {
        if (!request.headers.containsKey("Authorization")) {
            // don't overwrite, just add if not there.
            val jwt = authenticationService.getCurrentUser()!!.jwt
            request.headers.add("Authorization", "Bearer $jwt")
        }
        val response = execution.execute(request, body)
        return response
    }
}

And add it to the RestTemplate like so:

@Bean
fun restTemplate(): RestTemplate {
    val restTemplate = RestTemplate()
restTemplate.interceptors.add(RestTemplateHeaderModifierInterceptor(authenticationService)) // add interceptor to send JWT along with requests.
    return restTemplate
}

That way, every time you need a RestTemplate you can just use autowiring to get it. You do need to implement the AuthenticationService still to get the token from the TokenStore, like this:


val details = SecurityContextHolder.getContext().authentication.details
if (details is OAuth2AuthenticationDetails) {
   val token = tokenStore.readAccessToken(details.tokenValue)
   return token.value
}

  • 2
    Note: `request.headers.add("Authorization", "Bearer $jwt")` can be replaced by `request.headers.setBearerAuth(jwt)` since Spring 5.1 – Gediminas Rimsa Apr 03 '20 at 14:38
0

May be a little bit late but I think this is a common question, regarding Spring Security 6.0.0 for web client there is a class called ServletBearerExchangeFilterFunction that you can use to read the token from the security context and inject it.

@Bean
public WebClient rest() {
return WebClient.builder()
        .filter(new ServletBearerExchangeFilterFunction())
        .build();

For RestTemplate there is no automatic way and is recommended use a filter

@Bean
RestTemplate rest() {
  RestTemplate rest = new RestTemplate();
  rest.getInterceptors().add((request, body, execution) -> {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null) {
        return execution.execute(request, body);
    }

    if (!(authentication.getCredentials() instanceof AbstractOAuth2Token)) {
        return execution.execute(request, body);
    }

    AbstractOAuth2Token token = (AbstractOAuth2Token) authentication.getCredentials();
    request.getHeaders().setBearerAuth(token.getTokenValue());
    return execution.execute(request, body);
});
return rest;
}
Miguel Galindo
  • 111
  • 1
  • 3