简体   繁体   English

Spring 在 Feign 上没有 web 环境的情况下启动 OAuth2

[英]Spring Boot OAuth2 without web environment on Feign

I'm using Spring Boot 2.3.4 and I need to call an external web service that needs the oauth2 authentication.我正在使用 Spring Boot 2.3.4,我需要调用需要 oauth2 身份验证的外部 web 服务。

Currently I've achieved that in this way using feign目前我已经通过这种方式使用 feign 实现了这一点

Client客户

@FeignClient(name = "myClient", value = "myClient", url = "${app.my.client.apiUrl}", configuration = MyClientConfiguration.class)
    public interface MyClient {
    
    @GetMapping(value = "/api/my-url", consumes = "application/json")
    String getSomeData();
}

Client Configuration客户端配置

public class MyClientConfiguration {
    private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
    private final ClientRegistrationRepository clientRegistrationRepository;

        public MyClientConfiguration(OAuth2AuthorizedClientService oAuth2AuthorizedClientService, ClientRegistrationRepository clientRegistrationRepository) {
            this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService;
            this.clientRegistrationRepository = clientRegistrationRepository;
        }
    
        @Bean
        public RequestInterceptor requestInterceptor() {
            ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId("my-client");
            AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, oAuth2AuthorizedClientService);
            authorizedClientManager.setAuthorizedClientProvider(OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build());
            return new OAuthClientCredentialsRestTemplateInterceptor(authorizedClientManager, clientRegistration);
        }
    }

OAuth Interceptor OAuth拦截器

public class OAuthClientCredentialsRestTemplateInterceptor implements RequestInterceptor {
        private static final String BEARER_HEADER_NAME = "Bearer";
        private final OAuth2AuthorizedClientManager manager;
        private final Authentication emptyPrincipal;
        private final ClientRegistration clientRegistration;
    
        public OAuthClientCredentialsRestTemplateInterceptor(OAuth2AuthorizedClientManager manager, ClientRegistration clientRegistration) {
            this.manager = manager;
            this.clientRegistration = clientRegistration;
            this.emptyPrincipal = createEmptyPrincipal();
        }
    
        @Override
        public void apply(RequestTemplate requestTemplate) {
            OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistration.getRegistrationId()).principal(emptyPrincipal).build();
            OAuth2AuthorizedClient client = manager.authorize(oAuth2AuthorizeRequest);
            if (client == null)
                throw new IllegalStateException("Cannot retrieve a valid client for registration " + clientRegistration.getRegistrationId());
            requestTemplate.header(HttpHeaders.AUTHORIZATION, BEARER_HEADER_NAME + " " + client.getAccessToken().getTokenValue());
        }
    
        private Authentication createEmptyPrincipal() {
            return new Authentication() {
                @Override
                public Collection<? extends GrantedAuthority> getAuthorities() {
                    return Collections.emptySet();
                }
    
                @Override
                public Object getCredentials() {
                    return null;
                }
    
                @Override
                public Object getDetails() {
                    return null;
                }
    
                @Override
                public Object getPrincipal() {
                    return this;
                }
    
                @Override
                public boolean isAuthenticated() {
                    return false;
                }
    
                @Override
                public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
                }
    
                @Override
                public String getName() {
                    return clientRegistration.getClientId();
                }
            };
        }
    }

properties特性

spring:
  security:
    oauth2:
      client:
        registration:
          microsoft:
            client-id: ******
            client-secret:  ******
            scope:  ******
            authorization-grant-type: client_credentials
            provider: my-client
        provider:
          my-client:
            token-uri: ******

app:
  my-client:
    apiUrl: https://my-url.com

feign:
  hystrix:
    enabled: false
  client:
    config:
      default:
        connect-timeout: 3000

In another project I need the same BUT it's a spring boot application without the web environment , and I've the following error在另一个项目中,我需要相同的但它是一个没有 web 环境的 spring 启动应用程序,并且出现以下错误

bean of type 'org.springframework.security.oauth2.client.OAuth2AuthorizedClientService' that could not be found.

How can I solve this situation?我该如何解决这种情况?

Is it possible to use the oauth2 auto-configuration in an environment without a tomcat (or similar)?是否可以在没有 tomcat(或类似)的环境中使用 oauth2 自动配置?

You need to configure a separate class and tell spring IOC that this is stateless session.您需要配置一个单独的类并告诉 spring IOC 这是无状态会话。

@Configuration
@EnableWebSecurity
public class SecurityConfigForOauth extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().httpBasic().disable().formLogin().disable().logout().disable().sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

}

检查我关于如何让 client_credentials 在https://stackoverflow.com/a/65741386/698471 上工作的回复,对 spring-boot-starter-web 没有直接依赖

Not the solution, only a workaround to convert the microservice into a micro-application: let the job run at configuration time during application startup by either using no batch property or setting spring.batch.job.enabled=true and then terminating the process.不是解决方案,只是将微服务转换为微应用程序的解决方法:通过不使用批处理属性或设置spring.batch.job.enabled=true然后终止进程,让作业在应用程序启动期间的配置时间运行。

@SpringBootApplication
public class MyBootApp implements CommandLineRunner {

public static void main(String[] args) {
    SpringApplication.run(MyBootApp.class, args);
}

//Runs after @Configuration classes are finished synchronously including batch jobs.
@Override
public void run(String... args) throws Exception {
    System.exit(0); 
}

} }

It seems that RestTemplate and WebClient are using spring-web configuration files for autowiring stuff needed for OAuth interceptors.似乎 RestTemplate 和 WebClient 正在使用 spring-web 配置文件来自动装配 OAuth 拦截器所需的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM