首页 > 解决方案 > OAuth2 机器与 Spring WebClient 的机器

问题描述

为了让 Spring WebClient 针对(可能)OAuth2 端点对我的服务进行身份验证,我已经苦苦挣扎了 2 天。流程很简单,我需要从某个端点获取令牌并将其设置为授权标头。我认为springboot可以透明地做到这一点。

这是配置我的 Web 客户端的一段代码:

    private static final String SALES_FORCE = "sf";

    @Bean
    public WebClient salesforceClient(@Value("${salesforce.url}") String salesforceUrl,
            ReactiveClientRegistrationRepository clientRegistrationRepository, ReactiveOAuth2AuthorizedClientService clientService) {
        var clientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository, clientService);
        var oauth2Client = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientManager);
        oauth2Client.setDefaultClientRegistrationId(SALES_FORCE);

        return WebClient.builder()

                .baseUrl(salesforceUrl)

                .filter(oauth2Client)

                .build();
    }

这是我的透明身份验证配置:

spring.security.oauth2.client.registration.sf.client-id=<secret_id>
spring.security.oauth2.client.registration.sf.client-secret=<secret_key>
spring.security.oauth2.client.registration.sf.authorization-grant-type=refresh_token
spring.security.oauth2.client.registration.sf.user-info-authentication-method=form
spring.security.oauth2.client.provider.sf.token-uri=https://auth.exacttargetapis.com/v1/requestToken

我花了一段时间才弄清楚要使用哪些 oauth2 类,但最终应用程序启动了。我用 localhost 上的 WireMock 替换了 token-uri ,结果发现 Spring 甚至没有尝试检索令牌。然后我用 WireMock 替换了我的目标服务器并返回 401,假设这会触发令牌检索或刷新,但 Spring 仍然没有获取令牌。

我已经开始考虑手动管理这个令牌检索/过期,因为 Spring 对我来说似乎太神奇了。

我究竟做错了什么?

PS。

根据来自 ServerOAuth2AuthorizedClientExchangeFilterFunction 的 javadoc 建议,我还尝试了第二个构造函数,它(大概)也自动管理令牌过期。

    private static final String SALES_FORCE = "sf";

    @Bean
    public WebClient salesforceClient(@Value("${salesforce.url}") String salesforceUrl,
            ReactiveClientRegistrationRepository clientRegistrationRepository
            , ServerOAuth2AuthorizedClientRepository clientRepository) {
        var oauth2Client = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, clientRepository);
        oauth2Client.setDefaultClientRegistrationId(SALES_FORCE);
        
        return WebClient.builder()
                
                .baseUrl(salesforceUrl)
                
                .filter(oauth2Client)
                
                .build();
    }

但是在这里我的请求立即失败了

java.lang.IllegalArgumentException: serverWebExchange cannot be null
    at org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager.lambda$authorize$4(DefaultReactiveOAuth2AuthorizedClientManager.java:131)
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ Request to POST null [DefaultWebClient]
Stack trace:
        at ....

总而言之,与手动管理安全令牌相比,我觉得 springboot WebClient 配置和使用过于复杂。

标签: spring-bootoauth-2.0spring-webfluxwiremock

解决方案


切换ServerOAuth2AuthorizedClientRepository到 aReactiveOAuth2AuthorizedClientService将使代码运行:

@Bean
WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations,
                    ReactiveOAuth2AuthorizedClientService authorizedClientService) {
    ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrations, authorizedClientService));
    oauth.setDefaultClientRegistrationId("myRegistrationId");
    return WebClient.builder()
                    .filter(oauth)
                    .build();
}

建议使用ReactiveOAuth2AuthorizedClientManager构造函数并传入AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager.

资源


推荐阅读