弃用 spring-security-oauth 作为客户端
Deprecate spring-security-oauth as a client
我现在想从 spring-security-oauth 转移到 spring-security,但我找不到任何方法。
我搜索了很多,我能找到的都是关于提供 OAuth 端点的示例。
我目前的 OAuth2RestTemplate 有点复杂,因为 oauth 服务器没有使用标准方式进行识别,灵感来自 post here.
这是我的 OAuth2RestTemplate:
fun createOAuthRestTemplate(resourceDetails: OAuth2ProtectedResourceDetails): OAuth2RestTemplate {
val clientCredentialsAccessTokenProvider = ClientCredentialsAccessTokenProvider()
clientCredentialsAccessTokenProvider.setAuthenticationHandler(<myClientAuthenticationHandler extends ClientAuthenticationHandler>))
clientCredentialsAccessTokenProvider.setRequestFactory(requestFactory)
val oauthTemplate = OAuth2RestTemplate(resourceDetails)
oauthTemplate.setAccessTokenProvider(clientCredentialsAccessTokenProvider)
return oauthTemplate
}
不幸的是,spring migration guide 对我帮助不大,因为它只提到了 RestTemplate,但没有详细介绍。
[...]
A Simplified RestTemplate and WebClient
Spring Security OAuth extends RestTemplate, introducing OAuth2RestTemplate.
This class needs to be instantiated and exposed as a @Bean.
Spring Security chooses to favor composition and instead exposes an
OAuth2AuthorizedClientService, which is useful for creating RestTemplate interceptors
[...]
我现在的问题是:
如何使用 spring-security 在休息模板中获得相同的功能?
OAuth 2.0 with Spring Security 5 和 RestTemplate
Spring Security 5.2 不直接支持 RestTemplate
,但它具有简化工作的 bean。如果可以,建议是use WebClient
,而不是RestTemplate
。
但是,如果你需要使用RestTemplate
,那么你首先要创建一个OAuth2AuthorizedClientManager
:
@Bean
OAuth2AuthorizedClientManager authorizeClientManager(
ClientRegistrationRepository clients,
OAuth2AuthorizedClientRepository authorizedClients) {
DefaultOAuth2AuthorizedClientManager manager =
new DefaultOAuth2AuthorizedClientManager(clients, authorizedClients);
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
manager.setAuthorizedClientProvider(authorizedClientProvider);
return manager;
}
您可以在这个管理器 bean 中决定您希望拦截器为您协商的授权类型。它类似于 post.
中的 ClientCredentialsAccessTokenProvider
其次,您需要创建一个 RestTemplate
拦截器。它会要求经理给它一个令牌,然后将该令牌添加到 Authorization
header:
@Component
public class OAuth2AuthorizedClientInterceptor implements ClientHttpRequestInterceptor {
OAuth2AuthorizedClientManager manager;
public OAuth2AuthorizedClientInterceptor(OAuth2AuthorizedClientManager manager) {
this.manager = manager;
}
public ClientHttpResponse intercept(
HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
Authentication principal = // ...
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId("foo-client")
.principal(principal)
.build();
OAuth2AuthorizedClient authorizedClient =
this.manager.authorize(authorizedRequest);
HttpHeaders headers = httpRequest.getHeaders();
headers.setBearerAuth(authorizedClient.getAccessToken().getValue());
return execution.execute(request, body);
}
}
在Spring Security 5 中,每个客户端都由一个注册ID 表示。注册 ID 代替 foo-client
.
principal
将在一定程度上取决于您的情况,但通常使用 SecurityContextHolder.getContext().getAuthentication()
就足够了。如果上下文中没有用户,您可以考虑 AnonymousAuthenticationToken
,例如
Authentication principal = new AnonymousAuthenticationToken
("key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
最后,您可以将拦截器添加到 RestTemplate
:
@Bean
public RestTemplate rest(OAuth2AuthorizedClientInterceptor interceptor) {
RestTemplate rest = new RestTemplate();
rest.getInterceptors().add(interceptor);
return rest;
}
JWT 客户端认证
至于你说的post,是做一个JWT做客户端认证。对于此支持,您需要查看配置 DefaultClientCredentialsTokenResponseClient
。这将是您在第一步中构建的管理器的一部分:
@Bean
OAuth2AuthorizedClientManager authorizeClientManager(
ClientRegistrationRepository clients,
OAuth2AuthorizedClientRepository authorizedClients) {
DefaultOAuth2AuthorizedClientManager manager =
new DefaultOAuth2AuthorizedClientManager(clients, authorizedClients);
DefaultClientCredentialsTokenResponseClient tokenClient =
new DefaultClientCredentialsTokenResponseClient();
tokenClient.setRequestEntityConverter(fooConverter);
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials(cc -> cc
.accessTokenResponseClient(tokenClient))
.build();
manager.setAuthorizedClientProvider(authorizedClientProvider);
return manager;
}
fooConverter
是您为创建适当的 RequestEntity
实例而指定的任何转换器。类似于:
// ...
tokenClient.setRequestEntityConverter(grantRequest -> {
ClientRegistration client = grantRequest.getClientRegistration();
// ... formulate JWT
// ... create `RequestEntity`, including `Authorization` header
// that includes JWT as the bearer token
});
此 setter、setRequestEntityConverter
与遗留项目中 ClientAuthenticationHandler
的功能等效。
我现在想从 spring-security-oauth 转移到 spring-security,但我找不到任何方法。 我搜索了很多,我能找到的都是关于提供 OAuth 端点的示例。
我目前的 OAuth2RestTemplate 有点复杂,因为 oauth 服务器没有使用标准方式进行识别,灵感来自 post here.
这是我的 OAuth2RestTemplate:
fun createOAuthRestTemplate(resourceDetails: OAuth2ProtectedResourceDetails): OAuth2RestTemplate {
val clientCredentialsAccessTokenProvider = ClientCredentialsAccessTokenProvider()
clientCredentialsAccessTokenProvider.setAuthenticationHandler(<myClientAuthenticationHandler extends ClientAuthenticationHandler>))
clientCredentialsAccessTokenProvider.setRequestFactory(requestFactory)
val oauthTemplate = OAuth2RestTemplate(resourceDetails)
oauthTemplate.setAccessTokenProvider(clientCredentialsAccessTokenProvider)
return oauthTemplate
}
不幸的是,spring migration guide 对我帮助不大,因为它只提到了 RestTemplate,但没有详细介绍。
[...]
A Simplified RestTemplate and WebClient
Spring Security OAuth extends RestTemplate, introducing OAuth2RestTemplate.
This class needs to be instantiated and exposed as a @Bean.
Spring Security chooses to favor composition and instead exposes an
OAuth2AuthorizedClientService, which is useful for creating RestTemplate interceptors
[...]
我现在的问题是:
如何使用 spring-security 在休息模板中获得相同的功能?
OAuth 2.0 with Spring Security 5 和 RestTemplate
Spring Security 5.2 不直接支持 RestTemplate
,但它具有简化工作的 bean。如果可以,建议是use WebClient
,而不是RestTemplate
。
但是,如果你需要使用RestTemplate
,那么你首先要创建一个OAuth2AuthorizedClientManager
:
@Bean
OAuth2AuthorizedClientManager authorizeClientManager(
ClientRegistrationRepository clients,
OAuth2AuthorizedClientRepository authorizedClients) {
DefaultOAuth2AuthorizedClientManager manager =
new DefaultOAuth2AuthorizedClientManager(clients, authorizedClients);
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
manager.setAuthorizedClientProvider(authorizedClientProvider);
return manager;
}
您可以在这个管理器 bean 中决定您希望拦截器为您协商的授权类型。它类似于 post.
中的ClientCredentialsAccessTokenProvider
其次,您需要创建一个 RestTemplate
拦截器。它会要求经理给它一个令牌,然后将该令牌添加到 Authorization
header:
@Component
public class OAuth2AuthorizedClientInterceptor implements ClientHttpRequestInterceptor {
OAuth2AuthorizedClientManager manager;
public OAuth2AuthorizedClientInterceptor(OAuth2AuthorizedClientManager manager) {
this.manager = manager;
}
public ClientHttpResponse intercept(
HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
Authentication principal = // ...
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId("foo-client")
.principal(principal)
.build();
OAuth2AuthorizedClient authorizedClient =
this.manager.authorize(authorizedRequest);
HttpHeaders headers = httpRequest.getHeaders();
headers.setBearerAuth(authorizedClient.getAccessToken().getValue());
return execution.execute(request, body);
}
}
在Spring Security 5 中,每个客户端都由一个注册ID 表示。注册 ID 代替 foo-client
.
principal
将在一定程度上取决于您的情况,但通常使用 SecurityContextHolder.getContext().getAuthentication()
就足够了。如果上下文中没有用户,您可以考虑 AnonymousAuthenticationToken
,例如
Authentication principal = new AnonymousAuthenticationToken
("key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
最后,您可以将拦截器添加到 RestTemplate
:
@Bean
public RestTemplate rest(OAuth2AuthorizedClientInterceptor interceptor) {
RestTemplate rest = new RestTemplate();
rest.getInterceptors().add(interceptor);
return rest;
}
JWT 客户端认证
至于你说的post,是做一个JWT做客户端认证。对于此支持,您需要查看配置 DefaultClientCredentialsTokenResponseClient
。这将是您在第一步中构建的管理器的一部分:
@Bean
OAuth2AuthorizedClientManager authorizeClientManager(
ClientRegistrationRepository clients,
OAuth2AuthorizedClientRepository authorizedClients) {
DefaultOAuth2AuthorizedClientManager manager =
new DefaultOAuth2AuthorizedClientManager(clients, authorizedClients);
DefaultClientCredentialsTokenResponseClient tokenClient =
new DefaultClientCredentialsTokenResponseClient();
tokenClient.setRequestEntityConverter(fooConverter);
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials(cc -> cc
.accessTokenResponseClient(tokenClient))
.build();
manager.setAuthorizedClientProvider(authorizedClientProvider);
return manager;
}
fooConverter
是您为创建适当的 RequestEntity
实例而指定的任何转换器。类似于:
// ...
tokenClient.setRequestEntityConverter(grantRequest -> {
ClientRegistration client = grantRequest.getClientRegistration();
// ... formulate JWT
// ... create `RequestEntity`, including `Authorization` header
// that includes JWT as the bearer token
});
此 setter、setRequestEntityConverter
与遗留项目中 ClientAuthenticationHandler
的功能等效。