在@Async方法中授权OAuth2请求

问题描述 投票:0回答:2

我有一个使用 Spring Security、OAuth2 和 JWT 保护的 REST api。该服务也是 OAuth 客户端,因为它需要连接到其他服务(使用客户端凭据授予)。

使用 OpenFeign 完成对其他服务的请求,这些服务也受到 OAuth 的保护,这里是 OAuth2 的配置。

@Slf4j
@Configuration
public class OAuth2OpenFeignConfig {
    @Value("${client-name}")
    private String clientName;

    private final ClientRegistrationRepository clientRegistrationRepository;

    public OAuth2OpenFeignConfig(ClientRegistrationRepository clientRegistrationRepository) {
        this.clientRegistrationRepository = clientRegistrationRepository;
    }

    @Bean
    public RequestInterceptor requestInterceptor(OAuth2AuthorizedClientManager authorizedClientManager) {
        var clientRegistration = clientRegistrationRepository.findByRegistrationId(clientName);
        var clientCredentialsFeignManager = new OAuthClientCredentialsFeignManager();
        return requestTemplate -> requestTemplate.header("Authorization", "Bearer " + clientCredentialsFeignManager.getAccessToken(authorizedClientManager, clientRegistration));
    }

    static class OAuthClientCredentialsFeignManager {
        public String getAccessToken(OAuth2AuthorizedClientManager manager, ClientRegistration clientRegistration) {
            try {
                var oAuth2AuthorizeRequest = OAuth2AuthorizeRequest
                        .withClientRegistrationId(clientRegistration.getRegistrationId())
                        .principal(SecurityContextHolder.getContext().getAuthentication())
                        .build();
                var client = manager.authorize(oAuth2AuthorizeRequest);
                if (isNull(client)) {
                    throw new IllegalStateException("client credentials flow on " + clientRegistration.getRegistrationId() + " failed, client is null");
                }
                return client.getAccessToken().getTokenValue();
            } catch (Exception ex) {
                log.error("client credentials error " + ex.getMessage());
            }
            return null;
        }
    }
}

所有这些都在同步配置中按预期工作:假装配置请求并在标头中注入 JWT。当尝试使用异步重写调用时出现问题(Runnable 或 @Async 具有相同的结果)。

herehere所解释,安全上下文默认不会传播到其他线程,我需要手动配置

@Bean("threadPoolTaskExecutor")
public TaskExecutor getAsyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(20);
    executor.setMaxPoolSize(1000);
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.setThreadNamePrefix("Async-");
    executor.initialize(); // this is important, otherwise an error is thrown
    return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}

现在我可以看到上下文被传播到异步线程。然而,当尝试验证 OpenFeign 客户端时,会抛出异常

var client = manager.authorize(oAuth2AuthorizeRequest)

正如其内部调用的那样

HttpServletRequest servletRequest = getHttpServletRequestOrDefault(authorizeRequest.getAttributes());

但是

servletRequest
为空,并且请求中未设置凭据。

关于如何将 HttpContext 传递给线程有什么想法吗?我应该担心 https://docs.spring.io/spring-framework/docs/5.0.2.RELEASE/kdoc-api/spring-framework/org.springframework.web.filter/-request-context-filter /set-thread-context-inheritable.html ?

spring-boot asynchronous spring-security spring-security-oauth2 spring-cloud-feign
2个回答
2
投票

请查看AuthorizedClientServiceOAuth2AuthorizedClientManager。它能够在 servlet 上下文之外工作。


0
投票

以下代码对我来说非常有用

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
        ClientRegistrationRepository clientRegistrationRepository,
        OAuth2AuthorizedClientService authorizedClientRepository) {

    var authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .clientCredentials()
            .build();

    var authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(
            clientRegistrationRepository, 
            authorizedClientRepository);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

    return authorizedClientManager;
}
© www.soinside.com 2019 - 2024. All rights reserved.