You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

启用Circuit Breaker时如何获取并设置调用的Authorization Token?

Resilience4j 熔断器下Spring Security上下文传递解决方案

核心问题原因

Resilience4j的熔断器/线程池隔离默认运行在独立子线程中,不会自动继承主线程的Spring Security上下文,导致子线程发起请求时无法获取Authorization Token。以下是两种可行的解决方案:


方案一:自定义ThreadLocal传递Token

通过手动在主线程存储Token,再在子线程中恢复,确保请求头携带认证信息。

  1. 自定义上下文Holder存储Token
public class SecurityTokenHolder {
    private static final ThreadLocal<String> AUTH_TOKEN = new ThreadLocal<>();

    public static void setToken(String token) {
        AUTH_TOKEN.set(token);
    }

    public static String getToken() {
        return AUTH_TOKEN.get();
    }

    public static void clear() {
        AUTH_TOKEN.remove();
    }
}
  1. 请求拦截器在主线程存入Token
@Component
public class AuthTokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("Authorization");
        if (token != null) {
            SecurityTokenHolder.setToken(token);
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        SecurityTokenHolder.clear();
    }
}
  1. 配置Resilience4j线程池传递上下文
    通过装饰器将主线程的Token传递到子线程,执行完成后清理避免内存泄漏:
@Configuration
public class Resilience4jThreadPoolConfig {
    @Bean
    public ThreadPoolBulkhead myThreadPoolBulkhead() {
        ThreadPoolBulkheadConfig config = ThreadPoolBulkheadConfig.custom()
                .coreThreadPoolSize(10)
                .maxThreadPoolSize(20)
                .queueCapacity(50)
                .build();
        
        return ThreadPoolBulkhead.of("myServiceBulkhead", config, new TokenPropagatorDecorator());
    }

    static class TokenPropagatorDecorator implements ThreadPoolBulkhead.DelegatingDecorator {
        @Override
        public Runnable decorateRunnable(Runnable runnable) {
            String token = SecurityTokenHolder.getToken();
            return () -> {
                try {
                    SecurityTokenHolder.setToken(token);
                    runnable.run();
                } finally {
                    SecurityTokenHolder.clear();
                }
            };
        }

        @Override
        public <T> Callable<T> decorateCallable(Callable<T> callable) {
            String token = SecurityTokenHolder.getToken();
            return () -> {
                try {
                    SecurityTokenHolder.setToken(token);
                    return callable.call();
                } finally {
                    SecurityTokenHolder.clear();
                }
            };
        }
    }
}
  1. Feign客户端拦截器设置请求头
@Configuration
public class FeignAuthConfig {
    @Bean
    public RequestInterceptor feignTokenInterceptor() {
        return template -> {
            String token = SecurityTokenHolder.getToken();
            if (token != null) {
                template.header("Authorization", token);
            }
        };
    }
}

方案二:使用Resilience4j上下文传播扩展(Spring Boot 2.4+)

Resilience4j原生支持上下文传播,可直接传递Spring Security的SecurityContext。

  1. 实现自定义上下文传播器
@Component
public class SecurityContextPropagator implements ContextPropagator<SecurityContext> {
    @Override
    public Supplier<Optional<SecurityContext>> retrieve() {
        return () -> Optional.ofNullable(org.springframework.security.core.context.SecurityContextHolder.getContext());
    }

    @Override
    public Consumer<Optional<SecurityContext>> copy() {
        return context -> context.ifPresent(org.springframework.security.core.context.SecurityContextHolder::setContext);
    }

    @Override
    public Runnable clear() {
        return org.springframework.security.core.context.SecurityContextHolder::clearContext;
    }
}
  1. 配置文件开启上下文传播
    在application.yml中添加:
resilience4j:
  circuitbreaker:
    instances:
      myCircuitBreaker:
        context-propagation-enabled: true
  thread-pool-bulkhead:
    instances:
      myBulkhead:
        context-propagation-enabled: true
  1. Feign客户端直接读取SecurityContext
@Configuration
public class FeignAuthConfig {
    @Bean
    public RequestInterceptor feignAuthInterceptor() {
        return template -> {
            Authentication auth = org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication();
            if (auth != null && auth.getCredentials() instanceof String) {
                template.header("Authorization", (String) auth.getCredentials());
            }
        };
    }
}

注意事项

  • 若使用信号量隔离(SemaphoreBulkhead),请求在主线程执行,无需额外配置即可直接获取SecurityContext;
  • 线程池隔离场景下,必须确保上下文在任务执行后清理,避免线程复用导致的上下文污染。

内容的提问来源于stack exchange,提问作者Bruno Freitas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 04:18:29