Describe the bug spring boot version: 2.5.5 spring cloud version: 2020.0.4

Configuration Info:

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: FULL
      notify-service:
        requestInterceptors:
          - example.feign.BearerAuthRequestInterceptor
  circuitbreaker:
    enabled: true

RequestInterceptor :

public class BearerAuthRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        SecurityUtils.getCurrentUserJWT().ifPresent(
                token -> template.header("Authorization", String.format("Bearer %s", token))
        );
    }
}

After setting feign.circuitbreaker.enabled=true, check the log ,No Authorization Header information is included

[NotifyClient#createNotify] ---> POST http://notify-service/createNotify HTTP/1.1
2021-10-17 17:23:21.530 DEBUG 15069 --- [pool-2-thread-1] c.w.lab.skybrid.client.NotifyClient: [NotifyClient#createNotify] Content-Length: 495
2021-10-17 17:23:21.530 DEBUG 15069 --- [pool-2-thread-1] c.w.lab.skybrid.client.NotifyClient: [NotifyClient#createNotify] Content-Type: application/json

When setting feign.circuitbreaker.enabled=false, it works as expected

[NotifyClient#createNotify] ---> POST http://notify-service/createNotify HTTP/1.1
2021-10-17 17:28:29.526 DEBUG 18552 --- [nio-8080-exec-1] c.w.lab.skybrid.client.NotifyClient      : [NotifyClient#createNotify] Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjMsImlhdCI6MTYzNDA0NzE4NX0.l4VN7PnLGYd0TYAFer-1jNrrqE2vRDKqVAJ1GfG6Bqg
2021-10-17 17:28:29.526 DEBUG 18552 --- [nio-8080-exec-1] c.w.lab.skybrid.client.NotifyClient      : [NotifyClient#createNotify] Content-Length: 495
2021-10-17 17:28:29.526 DEBUG 18552 --- [nio-8080-exec-1] c.w.lab.skybrid.client.NotifyClient      : [NotifyClient#createNotify] Content-Type: application/json

Comment From: OlgaMaciaszek

Hello, @oursy. Thanks for reporting the issue. Please provide a minimal, complete, verifiable example that reproduces the issue.

Comment From: oursy

@OlgaMaciaszek Hi, I tried the smallest example locally and found that the cause of the problem was

  SecurityUtils.getCurrentUserJWT().ifPresent(
                 token -> template.header("Authorization", String.format("Bearer %s", token))
         );

  public static Optional<String> getCurrentUserJWT() {
         SecurityContext securityContext = SecurityContextHolder.getContext();
         return Optional
                 .ofNullable(securityContext.getAuthentication())
                 .filter(authentication -> authentication instanceof JwtAuthenticationToken)
                 .map(authentication -> ((JwtAuthenticationToken) authentication).getToken().getTokenValue());
     }

The reason for the problem is that the object obtained in the line SecurityContextHolder.getContext() is Null. When using Hystrix, you can use the configuration hystrix.shareSecurityContext: true parameter to share the security context. Is there a similar parameter configuration for circuitbreaker?

Comment From: OlgaMaciaszek

CircuitBreaker is an abstraction on top of various implementations. You should be able to use whichever setup the downstream library provides, so if you are using Hystrix, their setup, Resillience4J, their setup, etc., so hopefully you can find a solution with the implementation you've chosen. If you still need help with the CircuitBreakers setup that are not directly related to the OpenFeign integration, please create a separate issue in the https://github.com/spring-cloud/spring-cloud-circuitbreaker repo.

Comment From: oursy

The following code worked for me .

@Bean
    public Customizer<Resilience4jBulkheadProvider> defaultBulkheadCustomizer() {
        return provider -> provider.configureDefault(id -> new Resilience4jBulkheadConfigurationBuilder()
                .bulkheadConfig(BulkheadConfig.custom().build())
                .threadPoolBulkheadConfig(ThreadPoolBulkheadConfig.custom()
                        .contextPropagator(new SecurityBulkheadConfig())
                        .build())
                .build()
        );
    }
public class SecurityBulkheadConfig implements ContextPropagator<SecurityContext> {


    @Override
    public Supplier<Optional<SecurityContext>> retrieve() {
        return new Supplier<>() {
            @Override
            public Optional<SecurityContext> get() {
                return Optional.of(SecurityContextHolder.getContext());
            }
        };
    }


    @Override
    public Consumer<Optional<SecurityContext>> copy() {
        return new Consumer<Optional<SecurityContext>>() {
            @Override
            public void accept(Optional<SecurityContext> t) {
                t.ifPresent(SecurityContextHolder::setContext);
            }
        };
    }


    @Override
    public Consumer<Optional<SecurityContext>> clear() {
        return t -> t.ifPresent(t1 -> SecurityContextHolder.clearContext());
    }
}