Kotlin Spring Boot中WebClient注入为null,如何加载OAuth配置?
问题
我通过ReactiveClientRegistrationRepository为WebClient配置了OAuthClientConfiguration,但自动注入WebClient Bean时返回null,无法加载配置。
配置类代码:
@Configuration class OAuthClientConfiguration { @Bean fun clientRegistrations( @Value("\${spring.security.oauth2.client.provider.okta.access-token-url}") access_token: String?, @Value("\${spring.security.oauth2.client.registration.okta.client-id}") client_id: String?, @Value("\${spring.security.oauth2.client.registration.okta.client-secret}") client_secret: String?, @Value("\${spring.security.oauth2.client.registration.okta.scope}") scope: String?, @Value("\${spring.security.oauth2.client.registration.okta.authorization-grant-type}") authorizationGrantType: String? ): ReactiveClientRegistrationRepository? { val registration = ClientRegistration .withRegistrationId("okta") .tokenUri(access_token) .clientId(client_id) .clientSecret(client_secret) .scope(scope) .authorizationGrantType(AuthorizationGrantType(authorizationGrantType)) .build() return InMemoryReactiveClientRegistrationRepository(registration) } @Bean fun webClient(clientRegistrations: ReactiveClientRegistrationRepository?): WebClient? { val clientService = InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrations) val authorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrations, clientService) val oauth = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager) oauth.setDefaultClientRegistrationId("okta") return WebClient.builder() .filter(oauth) .build() } }
调用代码:
@Autowired private var webClient: WebClient? = null fun getAccessToken(): String? { return webClient?.post() ?.retrieve() ?.bodyToFlux(String::class.java) ?.onErrorMap { e: Throwable? -> Exception("message", e) } ?.blockLast(); }
希望创建带有该配置的WebClient,并在初始化时完成注入。
解决方案
1. 消除配置类中的空值风险
@Value注入的参数若配置缺失会返回null,直接用于构建ClientRegistration会导致Bean创建失败,进而WebClient无法注册。需做以下调整:
- 移除参数的可空标记,确保配置项必须存在(若允许配置缺失,可添加默认值)
- 拆分多scope字符串为数组,适配
ClientRegistration的scope方法要求
修改后的clientRegistrations方法:
@Bean fun clientRegistrations( @Value("\${spring.security.oauth2.client.provider.okta.access-token-url}") accessTokenUrl: String, @Value("\${spring.security.oauth2.client.registration.okta.client-id}") clientId: String, @Value("\${spring.security.oauth2.client.registration.okta.client-secret}") clientSecret: String, @Value("\${spring.security.oauth2.client.registration.okta.scope}") scope: String, @Value("\${spring.security.oauth2.client.registration.okta.authorization-grant-type}") authorizationGrantType: String ): ReactiveClientRegistrationRepository { val registration = ClientRegistration .withRegistrationId("okta") .tokenUri(accessTokenUrl) .clientId(clientId) .clientSecret(clientSecret) .scope(*scope.split(",").toTypedArray()) .authorizationGrantType(AuthorizationGrantType(authorizationGrantType)) .build() return InMemoryReactiveClientRegistrationRepository(registration) }
2. 确保WebClient Bean非空返回
Spring容器不会注册返回null的Bean,需将WebClient及依赖的ReactiveClientRegistrationRepository的返回类型改为非空:
修改后的webClient方法:
@Bean fun webClient(clientRegistrations: ReactiveClientRegistrationRepository): WebClient { val clientService = InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrations) val authorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager( clientRegistrations, clientService ) val oauthFilter = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager) oauthFilter.setDefaultClientRegistrationId("okta") return WebClient.builder() .filter(oauthFilter) .build() }
3. 改用构造注入避免空值
字段注入@Autowired可能在Bean未初始化完成时出现null,改用构造注入更可靠:
修改调用类:
class YourService(private val webClient: WebClient) { fun getTargetData(): String? { // 调用目标接口,OAuth过滤器会自动添加token到请求头 return webClient.get() .uri("https://your-target-api.com/data") .retrieve() .bodyToFlux(String::class.java) .onErrorMap { Exception("请求失败", it) } .blockLast() } }
4. 修正token获取逻辑
原代码中webClient.post()未指定请求URL,且ServerOAuth2AuthorizedClientExchangeFilterFunction会自动处理token的获取与刷新,无需手动调用token接口。若需直接获取token,可调整为:
fun getAccessToken(): Mono<OAuth2AccessToken> { val authorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager( clientRegistrations, InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrations) ) return authorizedClientManager.authorize( OAuth2AuthorizeRequest.withClientRegistrationId("okta") .principal(AnonymousAuthenticationToken("anonymous", "anonymous", emptyList())) .build() ).map { it.accessToken } }
内容的提问来源于stack exchange,提问作者Hamza
相关产品推荐
相关产品推荐

