tldr:安全上下文不会传播到由生成的链接未来 Mono.toFuture()
发出webclient请求时。
我有一个web应用程序,它使用SpringSecurityOAuth2在控制器上强制执行授权。该应用程序使用spring Responsive webclient将异步https请求编排到其他几个下游API,其中一个“repositorya”要求将调用者提供的令牌传播到下游调用。
为了简单地解释这一点,编排这些调用的服务如下所示:
public CompletableFuture<Model> doSomething(String id) {
return repositoryA.get(id)
.thenCompose(repositoryB::action)
.thenCompose(repositoryA::fulfill);
}
repositorya和repositoryb各自使用自己的 WebClient
,repositoryb通过一组客户端凭据处理其自己的下游身份验证。
存储库方法如下所示:
public CompletableFuture<Model> get(Stringid) {
return webClient.get()
.uri(b -> b.pathSegment(PATH, id.toString()).build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Model.class)
.toFuture();
}
repositorya的webclient使用筛选器将令牌作为安全上下文的头进行传播
public class DelegatingSecurityExchangeFilter implements ExchangeFilterFunction {
static final String SECURITY_CONTEXT_ATTRIBUTES = "org.springframework.security.SECURITY_CONTEXT_ATTRIBUTES";
@Override
public Mono<ClientResponse> filter(ClientRequest clientRequest, ExchangeFunction next) {
return Mono.deferContextual(view -> next.exchange(RequestWithBearer(clientRequest, getToken(view))));
}
private OAuth2AccessToken getToken(ContextView view) {
Map<Class<?>, Object> springCtx = view.get(SECURITY_CONTEXT_ATTRIBUTES);
Authentication auth = (Authentication) springCtx.get(Authentication.class);
return (OAuth2AccessToken) auth.getCredentials();
}
private ClientRequest RequestWithBearer(ClientRequest request, AbstractOAuth2Token token) {
return ClientRequest.from(request)
.headers(headers -> headers.setBearerAuth(token.getTokenValue()))
.build();
}
}
这在第一种情况下效果很好 repositoryA.get(id)
调用,但调试时我可以看到第一次 .toFuture()
在存储库方法中,安全上下文不会传播到下一个线程。
我已经研究了这里的许多概念,但到目前为止还没有成功,我假设被动webclient不使用相同的机制为其事件循环生成线程。 SecurityReactorContextConfiguration
看起来它应该在做这项工作,但它只有在subscribe上已有线程本地上下文时才起作用。
更多背景:
https://github.com/spring-projects/spring-framework/issues/25710
暂无答案!
目前还没有任何答案,快来回答吧!