【问题标题】:Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'request' is not active for the current thread for feign client创建名为“scopedTarget.oauth2ClientContext”的 bean 时出错:范围“请求”对于 feign 客户端的当前线程无效
【发布时间】:2019-06-06 05:44:59
【问题描述】:

一旦我当前的微服务启动并准备好在我当前使用 Jhipster 构建的微服务中使用 feign 客户端,我将调用另一个微服务。 所以我的 Feign 接口是

package com.persistent.integration.client;

import java.util.List;

import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.persistent.integration.service.dto.DataPipelineDTO;

@AuthorizedFeignClient(name = "Integrationconfiguration") 
public interface DataPipelinesResourceFeign {
     @RequestMapping(value = "/api/data-pipelines", method = RequestMethod.GET)     
     List<DataPipelineDTO> getAllDataPipelines(@RequestParam(value = "pageable") Pageable pageable );       
}

}

我已经实现了 ApplicationRunner,我在其中调用了 feign 客户端方法。

@Component
public class ApplicationInitializer implements ApplicationRunner {

    @Autowired
    private DataPipelinesResourceFeign dataPipelinesResourceFeign; 

    @Autowired
    private ActiveMQListener activeMqListener;


    @Override
    public void run(ApplicationArguments args) throws Exception {
        // TODO Auto-generated method stub
        Pageable pageable = PageRequest.of(0, 20);
        try {
        List <DataPipelineDTO> allStartedDataPipeLines = dataPipelinesResourceFeign.getAllDataPipelines(pageable);      //.stream().filter(p->p.getState().equals(State.STARTED)).collect(Collectors.toList());

        allStartedDataPipeLines.forEach(datapipe -> 
                {
                    try {
                        activeMqListener.consume(datapipe);
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                });
        } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
        }
    }

但运行后,它在 dataPipelinesResourceFeign.getAllDataPipelines 处给出以下异常:

com.netflix.hystrix.exception.HystrixRuntimeException: DataPipelinesResourceFeign#getAllDataPipelines(Pageable) failed and no fallback available.
    at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:819)
    at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804)
    at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140)
    at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
    at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
    at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1472)

引起:org.springframework.beans.factory.BeanCreationException: 创建名为“scopedTarget.oauth2ClientContext”的 bean 时出错: 范围“请求”对当前线程无效;考虑 如果您打算引用它,请为此 bean 定义一个作用域代理 来自单身人士;嵌套异常是 java.lang.IllegalStateException: 未找到线程绑定请求:您指的是请求属性吗 在实际 Web 请求之外,或在 最初的接收线程?如果您实际上是在 一个网络请求,仍然收到此消息,您的代码可能是 在 DispatcherServlet/DispatcherPortlet 之外运行:在这种情况下, 使用 RequestContextListener 或 RequestContextFilter 来公开 当前请求。在

org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(Abstract>tBeanFactory.java:362) 在 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractB>eanFactory.java:199) 在 org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTarge>tSource.java:35) 在 org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.>java:193) 在 com.sun.proxy.$Proxy147.getAccessToken(Unknown Source) 在 com.persistent.integration.security.oauth2.AuthorizationHeaderUtil.getAuthoriza>tionHeaderFromOAuth2Context(AuthorizationHeaderUtil.java:28) 在 com.persistent.integration.client.TokenRelayRequestInterceptor.apply(TokenRelay>RequestInterceptor.java:23) 在 feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:158) 在 feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:88) 在 feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) 在 feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108) 在 com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302) 在 com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298) 在 rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ... 68 更多原因:java.lang.IllegalStateException:否 找到线程绑定请求:您指的是请求属性吗 在实际 Web 请求之外,或在 最初的接收线程?如果您实际上是在 一个网络请求,仍然收到此消息,您的代码可能是 在 DispatcherServlet/DispatcherPortlet 之外运行:在这种情况下, 使用 RequestContextListener 或 RequestContextFilter 来公开 当前请求。在 org.springframework.web.context.request.RequestContextHolder.currentRequestAttr>ibutes(RequestContextHolder.java:131) 在 org.springframework.web.context.request.AbstractRequestAttributesScope.get(Abst>ractRequestAttributesScope.java:42) 在 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(Abstract>tBeanFactory.java:350)

互联网上的许多建议是添加侦听器 RequestContextListener。但是即使我在 onStartup 方法的 webConfigurer.java 中添加了监听器,问题仍然存在。

{

servletContext.addListener(RequestContextListener.class);

} 但是没用。

任何线索将不胜感激。

【问题讨论】:

    标签: spring-boot oauth-2.0 scope jhipster feign


    【解决方案1】:

    我找到了解决方法。我不知道为什么 TokenRelayRequestIntercepton 不起作用,但您可以使用自己的基于 Spring 的 SecurityContext 的 RequestInterceptor。

    首先,定义一个 RequestInterceptor :

        public class MyRequestInterceptor implements RequestInterceptor {
    
        public static final String AUTHORIZATION = "Authorization";
        public static final String BEARER = "Bearer";
    
        public MyRequestInterceptor() {
            super();
        }
    
        @Override
        public void apply(RequestTemplate template) {
            // demander un token à keycloak et le joindre à la request
            Optional<String> header = getAuthorizationHeader();
            if (header.isPresent()) {
                template.header(AUTHORIZATION, header.get());
            }
        }
    
        public static Optional<String> getAuthorizationHeader() {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication  != null && authentication.getDetails() != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
                OAuth2AuthenticationDetails oAuth2AuthenticationDetails =
                    (OAuth2AuthenticationDetails) authentication.getDetails();
    
                return Optional.of(String.format("%s %s", oAuth2AuthenticationDetails.getTokenType(),
                    oAuth2AuthenticationDetails.getTokenValue()));
    
            } else {
                return Optional.empty();
            }
        }
    
    }
    

    然后,使用 RequestInterceptor 为你的 feign 客户端声明一个配置类,它应该包含如下内容:

    @Bean(name = "myRequestInterceptor")
    public RequestInterceptor getMyRequestInterceptor() throws IOException {
        return new MyRequestInterceptor();
    }
    

    你的 Feign 客户端应该是这样的:

     @FeignClient(name = "SERVICE_NAME", configuration = MyFeignConfiguration.class)
    public interface MyRestClient {
    

    【讨论】:

      【解决方案2】:

      我使用 ApplicationRunner 在启动时运行 Feign Client 时遇到了同样的问题,我想出了以下解决方案。

      我用 OAuth2FeignRequestInterceptor 定义了我的 FeignClientsConfiguration,它接受预定义的 bean DefaultOAuth2ClientContext 和 OAuth2 配置 OAuth2ProtectedResourceDetails: p>

      @Configuration
      public class MyConfig extends FeignClientsConfiguration {
      
          @Bean
          public RequestInterceptor oauth2FeignRequestInterceptor(                                                           DefaultOAuth2ClientContext oAuth2ClientContext, MyOauth2Properties properties) {
              return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resourceDetails(properties));
          }
      
          @Bean
          public DefaultOAuth2ClientContext oAuth2ClientContext() {
              return new DefaultOAuth2ClientContext();
          }
      
          private OAuth2ProtectedResourceDetails resourceDetails(MyOauth2Properties oauth2Properties) {
              ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
              resourceDetails.setAccessTokenUri(oauth2Properties.getAccessTokenUri());
              resourceDetails.setUsername(oauth2Properties.getUsername());
              resourceDetails.setPassword(oauth2Properties.getPassword());
              resourceDetails.setClientId(oauth2Properties.getClientId());
              return resourceDetails;
          }
      }
      

      您的 feign 客户端将如下所示:

      @FeignClient(url = "http://localhost:8080/api/v1")
      public interface FeignClient {
      }
      

      毕竟,从 ApplicationRunner.run() 调用 FeignClient 工作正常。 Spring Boot 2.2.6

      【讨论】:

        猜你喜欢
        • 2018-10-28
        • 2011-09-21
        • 1970-01-01
        • 2019-12-01
        • 2016-06-22
        • 1970-01-01
        • 2020-09-28
        • 2017-09-06
        • 1970-01-01
        相关资源
        最近更新 更多