我正在使用 Spring Security 5.5 来执行访问令牌请求,最近升级到 5.5.1,现在我的客户端机密被我的 OAuth 2.0 提供商拒绝。这是由 a bug fix 根据 RFC 6749 Section 2.3.1 对客户端凭据进行 URL 编码造成的。
由于我的 OAuth 2.0 提供程序不合规,我想恢复到 Spring Security 5.5.0 中的旧行为,并发送不带 URL 编码的客户端凭据。
从 reference documentation 中,如果我定义一个 @Bean
类型的 OAuth2AuthorizedClientManager
:
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
// @formatter:off
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// @formatter:on
return authorizedClientManager;
}
如何配置它以使用自定义转换器来设置凭据?
注意:与 this question 有关,但解决了 Servlet 支持而不是 WebClient 的响应式支持。
答案 0 :(得分:0)
假设您有以下配置:
spring:
security:
oauth2:
client:
registration:
test-client:
provider: spring
client-id: aladdin
client-secret: "open sesame"
authorization-grant-type: client_credentials
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
scope: resource:read
provider:
spring:
authorization-uri: http://auth-server:9000/oauth/authorize
token-uri: http://auth-server:9000/oauth/token
在 OP 的示例中,OAuth2AuthorizedClientManager
支持 client_credentials
授予以发出访问令牌请求。这对前很有用。如果我们想实现以下(虚构的)端点:
@RestController
public class TokenController {
@GetMapping("/token")
public OAuth2AccessToken token(@RegisteredOAuth2AuthorizedClient("test-client") OAuth2AuthorizedClient testClient) {
return testClient.getAccessToken();
}
}
OAuth2AuthorizedClientManager
提供了本示例中注入的 OAuth2AuthorizedClient
。它可以使用自定义转换器进行配置,如下所示:
注意:以下是改编自参考文档 Client Credentials section 的扩展示例。
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
// @formatter:off
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials((builder) ->
builder.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient())
.build())
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// @formatter:on
return authorizedClientManager;
}
@Bean
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
// @formatter:off
OAuth2ClientCredentialsGrantRequestEntityConverter requestEntityConverter =
new OAuth2ClientCredentialsGrantRequestEntityConverter();
requestEntityConverter.setHeadersConverter(headersConverter());
DefaultClientCredentialsTokenResponseClient accessTokenResponseClient =
new DefaultClientCredentialsTokenResponseClient();
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);
// @formatter:on
return accessTokenResponseClient;
}
private static <T extends AbstractOAuth2AuthorizationGrantRequest> Converter<T, HttpHeaders> headersConverter() {
// @formatter:off
Converter<T, ClientRegistration> clientRegistrationConverter =
AbstractOAuth2AuthorizationGrantRequest::getClientRegistration;
return clientRegistrationConverter
.andThen((clientRegistration) -> {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8));
headers.setContentType(MediaType.valueOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8"));
headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());
return headers;
});
// @formatter:on
}
如果我们想支持 authorization_code
授予登录应用程序,我们可以使用以下配置(包含上述方法和 @Bean
定义):
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(authorizeRequests -> authorizeRequests
.anyRequest().authenticated())
.oauth2Login(oauth2Login -> oauth2Login
.tokenEndpoint(tokenEndpoint -> tokenEndpoint
.accessTokenResponseClient(authorizationCodeAccessTokenResponseClient())));
// @formatter:on
return http.build();
}
@Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
// @formatter:off
OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter =
new OAuth2AuthorizationCodeGrantRequestEntityConverter();
requestEntityConverter.setHeadersConverter(headersConverter());
DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new DefaultAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);
// @formatter:on
return accessTokenResponseClient;
}
// ...
}