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

如何在非Web Spring Boot批处理应用中配置ClientRegistrationRepository

Spring Boot批处理禁用Web服务器后OAuth2客户端配置问题解决

问题背景

使用Spring Boot 2.7.1 + spring-boot-starter-batch开发批处理任务,需要两个带不同认证机制的WebClient调用外部API,配置依赖spring.security.oauth2.client系列配置项。

原本引入spring-boot-starter-web后,Spring能自动配置WebClient(依赖ClientRegistrationRepository Bean),但批处理会默认监听8080端口,无法并行启动多个实例,因此需要禁用Web服务器。

禁用Web服务器后(通过配置、代码或移除spring-boot-starter-web依赖),批处理启动失败,报错:

a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' that could not be found

核心原因是OAuth2ClientAutoConfiguration带有条件注解,要求应用类型为SERVLET,而禁用Web服务器后应用类型变为NONE,导致自动配置不生效:

@AutoConfiguration(before = SecurityAutoConfiguration.class)
@ConditionalOnClass({ EnableWebSecurity.class, ClientRegistration.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@Import({ OAuth2ClientRegistrationRepositoryConfiguration.class, OAuth2WebSecurityConfiguration.class })
public class OAuth2ClientAutoConfiguration {

}

尝试通过@ImportAutoConfiguration(OAuth2ClientAutoConfiguration.class)强制加载无效,且其导入的两个配置类为非公开,无法直接导入。


解决方法

方案1:手动配置ClientRegistrationRepository及关联Bean

直接读取spring.security.oauth2.client配置,手动创建所需Bean,绕开自动配置的条件限制:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
public class OAuth2ClientConfig {

    @Bean
    @ConfigurationProperties("spring.security.oauth2.client")
    public ClientRegistrationProperties clientRegistrationProperties() {
        return new ClientRegistrationProperties();
    }

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository(ClientRegistrationProperties properties) {
        List<ClientRegistration> registrations = properties.getRegistration().values().stream()
                .map(this::convertToClientRegistration)
                .toList();
        return new InMemoryClientRegistrationRepository(registrations);
    }

    private ClientRegistration convertToClientRegistration(ClientRegistrationProperties.Registration registration) {
        return ClientRegistration.withRegistrationId(registration.getRegistrationId())
                .clientId(registration.getClientId())
                .clientSecret(registration.getClientSecret())
                .clientAuthenticationMethod(ClientAuthenticationMethod.valueOf(registration.getClientAuthenticationMethod()))
                .authorizationGrantType(AuthorizationGrantType.valueOf(registration.getAuthorizationGrantType()))
                .redirectUri(registration.getRedirectUri())
                .scope(registration.getScope().toArray(new String[0]))
                .authorizationUri(registration.getAuthorizationUri())
                .tokenUri(registration.getTokenUri())
                .userInfoUri(registration.getUserInfoUri())
                .userNameAttributeName(registration.getUserNameAttributeName())
                .jwkSetUri(registration.getJwkSetUri())
                .clientName(registration.getClientName())
                .build();
    }
}

注:ClientRegistrationProperties是Spring Boot提供的配置绑定类,用于读取spring.security.oauth2.client下的所有配置项。

方案2:强制设置应用类型为SERVLET并禁用Web服务器

通过配置让Spring Boot认为应用是SERVLET类型,同时禁用Web服务器端口,满足OAuth2ClientAutoConfiguration的条件:

在application.yml中添加:

spring:
  main:
    web-application-type: servlet
server:
  port: -1 # 禁用Web服务器端口

或者在启动类中硬编码设置:

@SpringBootApplication
public class BatchApplication {
    public static void main(String[] args) {
        new SpringApplicationBuilder(BatchApplication.class)
                .web(WebApplicationType.SERVLET)
                .run(args);
    }
}

这种方式会触发OAuth2ClientAutoConfiguration的自动配置逻辑,同时不会启动Web服务器监听端口。

方案3:自定义WebClient并直接集成OAuth2认证

如果仅需要WebClient具备OAuth2认证能力,可直接为每个WebClient配置认证逻辑,无需依赖自动配置的ClientRegistrationRepository:

import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebClientConfig {

    // 对应第一个API的WebClient
    @Bean("api1WebClient")
    public WebClient api1WebClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Filter =
                new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oauth2Filter.setDefaultClientRegistrationId("api1"); // 对应配置中的registrationId
        return WebClient.builder()
                .filter(oauth2Filter)
                .baseUrl("https://api1.example.com")
                .build();
    }

    // 对应第二个API的WebClient
    @Bean("api2WebClient")
    public WebClient api2WebClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Filter =
                new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oauth2Filter.setDefaultClientRegistrationId("api2"); // 对应配置中的registrationId
        return WebClient.builder()
                .filter(oauth2Filter)
                .baseUrl("https://api2.example.com")
                .build();
    }

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientRepository authorizedClientRepository) {
        OAuth2AuthorizedClientProvider authorizedClientProvider =
                OAuth2AuthorizedClientProviderBuilder.builder()
                        .clientCredentials() // 根据实际授权类型调整,如authorization_code
                        .build();
        DefaultOAuth2AuthorizedClientManager authorizedClientManager =
                new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
        return authorizedClientManager;
    }
}

配合application.yml配置:

spring:
  security:
    oauth2:
      client:
        registration:
          api1:
            client-id: your-api1-client-id
            client-secret: your-api1-client-secret
            authorization-grant-type: client_credentials
            scope: read,write
          api2:
            client-id: your-api2-client-id
            client-secret: your-api2-client-secret
            authorization-grant-type: client_credentials
            scope: read
        provider:
          api1:
            token-uri: https://api1.example.com/oauth/token
          api2:
            token-uri: https://api2.example.com/oauth/token

内容的提问来源于stack exchange,提问作者Vincent F

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 13:45:53