Implementing Token Relay in Spring Cloud Feign with Resilience4J
This guide explains why token relay is essential for Spring Cloud microservice calls, how Feign’s default behavior blocks token propagation when circuit breaking is enabled, and provides a step‑by‑step solution using a custom RequestInterceptor and InheritableThreadLocal to forward JWT tokens across threads.
In the previous article we customized a Spring Cloud resource server, but discovered that token relay is required for microservice calls to preserve user authentication across the call chain. Token relay (Token Relay) simply means passing the JWT token between services so the resource server can authenticate the caller.
Token Relay
If a client calls service A with a token, A can authenticate the request, but when A calls service B via Feign the token is not automatically forwarded.
Typical Feign client definition:
@FeignClient(name = "foo-service", fallback = FooClient.Fallback.class)
public interface FooClient {
@GetMapping("/foo/bar")
Rest<Map<String, String>> bar();
@Component
class Fallback implements FooClient {
@Override
public Rest<Map<String, String>> bar() {
return RestBody.fallback();
}
}
}Without circuit breaking, we can extract the JWT from SecurityContextHolder and add it to the request header via a Feign RequestInterceptor:
/**
* Needs Spring IoC injection
*/
static class BearerTokenRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
final String authorization = HttpHeaders.AUTHORIZATION;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof JwtAuthenticationToken) {
JwtAuthenticationToken jwt = (JwtAuthenticationToken) authentication;
String tokenValue = jwt.getToken().getTokenValue();
template.header(authorization, "Bearer " + tokenValue);
}
}
}When circuit breaking (e.g., Resilience4J) is enabled, Feign calls run in a separate thread, and SecurityContextHolder (which stores data in a ThreadLocal) cannot be accessed, breaking token relay.
❝ Circuit breaker libraries such as Hystrix, Resilience4J, and Sentinel have slightly different mechanisms. ❞
How to Achieve Token Relay
Inspecting the Feign proxy handler FeignCircuitBreakerInvocationHandler reveals that it captures the current request attributes before switching threads:
private Supplier<Object> asSupplier(final Method method, final Object[] args) {
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return () -> {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return this.dispatch.get(method).invoke(args);
} catch (RuntimeException throwable) {
throw throwable;
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}This code stores the original request attributes (including the HTTP request) and restores them in the child thread, enabling data propagation.
InheritableThreadLocal
RequestContextHoldermaintains two containers: a regular ThreadLocal and an InheritableThreadLocal (implemented as NamedInheritableThreadLocal). The latter allows parent‑thread data to be inherited by child threads, which we can leverage for token relay.
public abstract class RequestContextHolder {
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
// ...
}By using the inheritable holder, the request information (including the JWT) is available in the circuit‑breaker thread.
Final Implementation
Modify the original Feign interceptor to obtain the token from the current request attributes and set it on the outgoing Feign request:
/**
* Token relay interceptor
*/
static class BearerTokenRequestInterceptor implements RequestInterceptor {
private static final Pattern BEARER_TOKEN_HEADER_PATTERN =
Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-._~+/]+=*)$", Pattern.CASE_INSENSITIVE);
@Override
public void apply(RequestTemplate template) {
final String authorization = HttpHeaders.AUTHORIZATION;
ServletRequestAttributes requestAttributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
String authHeader = requestAttributes.getRequest().getHeader(HttpHeaders.AUTHORIZATION);
Matcher matcher = BEARER_TOKEN_HEADER_PATTERN.matcher(authHeader);
if (matcher.matches()) {
// Remove existing token header to avoid duplication
template.header(authorization);
template.header(authorization, authHeader);
}
}
}
}Now, when FooClient.bar() is called, the downstream service (the OAuth2 Resource Server) receives the original JWT and can perform user‑based authorization.
❝ Remember to register this interceptor as a Spring bean. ❞
Summary
Token relay is crucial for preserving user context across microservice call chains. By leveraging Feign’s interceptor mechanism and the InheritableThreadLocal feature of RequestContextHolder, we can forward JWT tokens even when circuit breakers like Resilience4J execute calls in separate threads.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
