我在spring中实现了一个资源服务器,需要配置一个webclient,以便从其他api访问下游数据。该webclient需要协商oauth2流的客户端凭据。相当典型的东西。我感到困惑的是:
为此配置webclient时,我首先定义一个适当的 ReactiveOAuth2AuthorizedClientManager
. 然后我定义一个合适的 ExchangeFilterFunction
,用我的客户机管理器初始化它。根据spring的文档(并通过查看源代码进行确认),在以这种方式初始化filter函数时,我仍然需要显式地设置 authorizationFailureHandler
即使客户经理也有同样的想法 authorizationFailureHandler
自动配置。
我是不是遗漏了为什么要这么做?也许这是 Spring 保安的问题。
这是我的密码:
@Bean
public WebClient webClientForSomeDownstreamAPI(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ReactiveOAuth2AuthorizedClientService authorizedClientService) {
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository,
authorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build());
ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler =
new RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler(
(clientRegistrationId, principal, attributes) ->
authorizedClientService.removeAuthorizedClient(
clientRegistrationId, principal.getName()));
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
//for some reason the filter function and the client manager both need a failure handler
oauth.setAuthorizationFailureHandler(authorizationFailureHandler);
oauth.setDefaultClientRegistrationId("redacted");
return WebClient.builder()
.filter(oauth)
.clientConnector(createClientConnectorWithLogging())
.baseUrl("redacted")
.build();
}
Spring的 AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager
(天哪,真是满嘴废话)使用对传入的文件的引用自动配置故障处理程序 authorizedClientService
如下面的代码片段所示(取自源代码)。
public AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ReactiveOAuth2AuthorizedClientService authorizedClientService) {
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
Assert.notNull(authorizedClientService, "authorizedClientService cannot be null");
this.clientRegistrationRepository = clientRegistrationRepository;
this.authorizedClientService = authorizedClientService;
this.authorizationSuccessHandler = (authorizedClient, principal, attributes) -> authorizedClientService
.saveAuthorizedClient(authorizedClient, principal);
this.authorizationFailureHandler = new RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler(
(clientRegistrationId, principal, attributes) -> this.authorizedClientService
.removeAuthorizedClient(clientRegistrationId, principal.getName()));
}
但是 ServerOAuth2AuthorizedClientExchangeFilterFunction
我使用的构造函数(将客户机管理器作为参数)需要我随后显式地设置失败处理程序。请参见javadoc中的以下片段:
/**
* When this constructor is used, authentication (HTTP 401) and authorization (HTTP
* 403) failures returned from a OAuth 2.0 Resource Server will <em>NOT</em> be
* forwarded to a {@link ReactiveOAuth2AuthorizationFailureHandler}. Therefore, future
* requests to the Resource Server will most likely use the same (most likely invalid)
* token, resulting in the same errors returned from the Resource Server. It is
* recommended to configure a
* {@link RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler} via
* {@link #setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler)}
* so that authentication and authorization failures returned from a Resource Server
* will result in removing the authorized client, so that a new token is retrieved for
* future requests.
* /
有趣的是另一个filter函数构造函数确实创建了一个 authorizationFailureHandler
它几乎和客户经理自动创建的内容一样。请看这里:
public ServerOAuth2AuthorizedClientExchangeFilterFunction(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler = new RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler(
(clientRegistrationId, principal, attributes) -> authorizedClientRepository.removeAuthorizedClient(
clientRegistrationId, principal,
(ServerWebExchange) attributes.get(ServerWebExchange.class.getName())));
this.authorizedClientManager = createDefaultAuthorizedClientManager(clientRegistrationRepository,
authorizedClientRepository, authorizationFailureHandler);
this.clientResponseHandler = new AuthorizationFailureForwarder(authorizationFailureHandler);
this.defaultAuthorizedClientManager = true;
}
我知道客户经理不会公开 authorizationFailureHandler
所以我调用的过滤器函数构造函数不能仅仅引用它。但这是怎么回事?让两个失败处理程序对同一个对象执行相同的操作是不对的 authorizedClientService
所以我肯定错过了什么。
我使用的是springboot2.4.1和springsecurity 5.4.2。
暂无答案!
目前还没有任何答案,快来回答吧!