Spring Security 6.5.1:配置ClientCredentials令牌请求超时(RestClient方式)
问题
使用Spring Security OAuth2 Client 6.5.1的ClientCredentials授权类型时,需要为令牌请求设置连接/读取超时。官方推荐的RestClientClientCredentialsTokenResponseClient在自定义RestClient并通过setRestClient()设置时,会丢失默认的消息转换器(如FormHttpMessageConverter、OAuth2AccessTokenResponseHttpMessageConverter)和错误处理器,导致令牌解析失败。
想知道有没有无需覆盖默认配置就能添加超时的简洁方式?当前环境是Spring Boot 3.2.5、Spring Security 6.5.1。
附上当前代码:
@Configuration public class OAuth2ClientConfig { private RestClient createTimeoutRestClient() { FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter(); OAuth2AccessTokenResponseHttpMessageConverter tokenResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); return RestClient.builder() //自定义超时设置 .requestFactory(() -> { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setConnectTimeout(3000); requestFactory.setReadTimeout(5000); return requestFactory; }) //默认配置,复制自RestClientClientCredentialsTokenResponseClient(); .messageConverters(converters -> { converters.clear(); converters.add(formHttpMessageConverter); converters.add(tokenResponseConverter); }) .defaultStatusHandler(new OAuth2ErrorResponseErrorHandler()) .build(); } private RestClientClientCredentialsTokenResponseClient createTokenResponseClient() { RestClientClientCredentialsTokenResponseClient tokenResponseClient = new RestClientClientCredentialsTokenResponseClient(); tokenResponseClient.setRestClient(createTimeoutRestClient()); return tokenResponseClient; } @Bean public OAuth2AuthorizedClientManager authorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientService authorizedClientService) { ClientCredentialsOAuth2AuthorizedClientProvider provider = new ClientCredentialsOAuth2AuthorizedClientProvider(); provider.setAccessTokenResponseClient(createTokenResponseClient()); AuthorizedClientServiceOAuth2AuthorizedClientManager manager = new AuthorizedClientServiceOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientService); manager.setAuthorizedClientProvider(provider); return manager; } }
另外,是否有遗漏的Spring Security或Spring Boot内置API可用于配置OAuth2令牌请求超时?希望得到最佳实践或代码示例。
解决方案
方案1:复用默认RestClient配置,仅修改超时
不需要完全重新创建RestClient,而是获取RestClientClientCredentialsTokenResponseClient默认生成的RestClient,通过mutate()方法仅替换请求工厂添加超时,这样能完整保留原有的消息转换器和错误处理器。
代码示例:
@Configuration public class OAuth2ClientConfig { private RestClientClientCredentialsTokenResponseClient createTokenResponseClient() { RestClientClientCredentialsTokenResponseClient tokenResponseClient = new RestClientClientCredentialsTokenResponseClient(); // 基于默认RestClient修改请求工厂,添加超时配置 RestClient modifiedRestClient = tokenResponseClient.getRestClient().mutate() .requestFactory(() -> { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setConnectTimeout(3000); requestFactory.setReadTimeout(5000); return requestFactory; }) .build(); tokenResponseClient.setRestClient(modifiedRestClient); return tokenResponseClient; } @Bean public OAuth2AuthorizedClientManager authorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientService authorizedClientService) { ClientCredentialsOAuth2AuthorizedClientProvider provider = new ClientCredentialsOAuth2AuthorizedClientProvider(); provider.setAccessTokenResponseClient(createTokenResponseClient()); AuthorizedClientServiceOAuth2AuthorizedClientManager manager = new AuthorizedClientServiceOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientService); manager.setAuthorizedClientProvider(provider); return manager; } }
方案2:使用Spring Boot配置属性(推荐)
Spring Boot 3.2+支持通过配置文件直接为OAuth2客户端的令牌请求设置超时,无需编写Java配置,完全遵循约定大于配置原则,代码侵入性最低。
在application.yml中添加配置:
spring: security: oauth2: client: provider: # 替换为你的授权服务器提供者ID your-provider-id: token-uri: https://your-auth-server/token # 配置令牌请求的连接和读取超时 token-request: connect-timeout: 3s read-timeout: 5s registration: # 替换为你的客户端ID your-client-id: provider: your-provider-id client-id: your-client-id client-secret: your-client-secret authorization-grant-type: client_credentials
或application.properties:
spring.security.oauth2.client.provider.your-provider-id.token-uri=https://your-auth-server/token spring.security.oauth2.client.provider.your-provider-id.token-request.connect-timeout=3s spring.security.oauth2.client.provider.your-provider-id.token-request.read-timeout=5s spring.security.oauth2.client.registration.your-client-id.provider=your-provider-id spring.security.oauth2.client.registration.your-client-id.client-id=your-client-id spring.security.oauth2.client.registration.your-client-id.client-secret=your-client-secret spring.security.oauth2.client.registration.your-client-id.authorization-grant-type=client_credentials
这种方式会自动配置RestClientClientCredentialsTokenResponseClient的超时参数,同时保留所有默认的消息转换器和错误处理器。
最佳实践
- 优先使用方案2的配置属性方式,代码侵入性低,易于维护和后续版本升级。
- 若需要更复杂的RestClient自定义(如添加拦截器等),再使用方案1的
mutate()方式复用默认配置,避免手动复制默认组件带来的适配成本。
内容的提问来源于stack exchange,提问作者winston

