Spring Security 5.1-使用WebClient获取客户端凭证流的令牌

时间:2018-11-07 13:52:41

标签: java spring spring-boot spring-security

我正在尝试通过以下设置通过webclient获取承载令牌,以对Servlet应用程序中的受保护资源服务器进行集成测试。

spring:
  security:
    oauth2:
      client:
        registration:
          idp:
            clientId: id
            clientSecret: secret
            authorization-grant-type: client_credentials
            scope: read
        provider:
          idp:
            authorization-uri: myidp/authorization.oauth2
            token-uri: myidp/token.oauth2
            user-info-uri: myidp/userinfo.openid
            user-name-attribute: name

还有豆子

    @Bean
    WebClient webClient(ClientRegistrationRepository clientRegistrations,
            OAuth2AuthorizedClientRepository authorizedClients) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
                clientRegistrations, authorizedClients);
        // (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
        // oauth.setDefaultOAuth2AuthorizedClient(true);
        // (optional) set a default ClientRegistration.registrationId
        // oauth.setDefaultClientRegistrationId("client-registration-id");
        return WebClient.builder().apply(oauth.oauth2Configuration()).build();
    }

并自动将Web客户端连接到测试并像这样调用它,

webClient.get().uri("http://localhost:" + port + "/web/it")
                .attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId("idp")).retrieve()
                .bodyToMono(String.class).block();

我假设交换功能将获取访问令牌(如果可用),或者进行调用以从IDP获取新令牌。但是,由于HttpSessionOAuth2AuthorizedClientRepository为空,因此它总是会失败,因为HttpServletRequest

总体测试看起来像这样,它可以自动配置以为IDP提供程序配置一些bean。

@SpringBootTest(classes = WebITApplication.class,
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(SpringExtension.class)
@ActiveProfiles("web-it")
class WebJwtIt {

    @LocalServerPort
    private int port;

    @Autowired
    private WebClient webClient;

    @Test
    void testIdpJwt() {

        String response = webClient.get().uri("http://localhost:" + port + "/web/it")
                .attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId("ping")).retrieve()
                .bodyToMono(String.class).block();
        assertThat(response).isEqualTo("pass");
    }

    @RestController
    @SpringBootApplication
    @ImportAutoConfiguration(IdpAutoConfiguration.class)
    static class WebITApplication implements IdpSecurityAdapter {

              @Bean
    WebClient webClient(ClientRegistrationRepository clientRegistrations,
            OAuth2AuthorizedClientRepository authorizedClients) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
                clientRegistrations, authorizedClients);
        // (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
        // oauth.setDefaultOAuth2AuthorizedClient(true);
        // (optional) set a default ClientRegistration.registrationId
        // oauth.setDefaultClientRegistrationId("client-registration-id");
        return WebClient.builder().apply(oauth.oauth2Configuration()).build();
    }
        public static void main(String args[]) {

            new SpringApplicationBuilder().profiles("web-it").build().run(WebITApplication.class, args);
        }

        @GetMapping
        public String secured() {
            return "secured";
        }

        @GetMapping("/web/it")
        public String securedOne() {
            return "pass";
        }

        @Override
        public void configure(final HttpSecurity httpSecurity) throws IdpSecurityAdapterException {
            try {
                httpSecurity.oauth2Client();
            } catch (Exception e) {
                throw new IdpSecurityAdapterException("Failed to Configure Oauth2Client", e);
            }
        }

        @Override
        public IdpProvider getIdpProvider() {
            return IdpProvider.MyIdp;
        }
    }

反正Web客户端是否可以为我获取令牌并将其添加到请求中?我知道spring-security-oauth:OAuthRestTemplate可以做到这一点,并阅读我认为可以通过Web客户端实现的文档。

1 个答案:

答案 0 :(得分:3)

这里的问题是您没有以正确的方式实例化WebClient。

由于您位于客户端,因此您无权访问OAuth2AuthorizedClientRepository。该bean应该链接到您在.oauth2Login()配置上使用HttpSecurity方法声明登录到的资源服务器。这些详细信息在这里说明:Spring Security 5 Oauth2 Login

同样,您位于客户端,因此您需要的是交换过滤器功能,该功能将触发向授权服务器的请求以获取JWT令牌。您可以改用ServerOAuth2AuthorizedClientExchangeFilterFunction

最好使用WebClientCustomizer在WebClient过滤器中添加交换过滤器功能。为什么呢仅仅因为在Spring应用程序中注入WebClient.Builder,您就可以访问链接到网络交易的本机指标。

因此,您将使用UnAuthenticatedServerOAuth2AuthorizedClientRepository bean的新实例来构建WebClient,如下所示:

// Use injection to get an in-memory reposiroty or client registrations
@Bean
WebClient webClient(ClientRegistrationRepository clientRegistrations) {

    // Provides support for an unauthenticated user such as an application
    ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
            clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());

    // Build up a new WebClientCustomizer implementation to inject the oauth filter
    // function into the WebClient.Builder instance
    return new WebClientSecurityCustomizer(oauth);
}

由于您位于客户端上,因此没有将用户与流程相关联,这就是为什么您不能使用任何授权的客户端存储库bean实例化的原因。查看Spring Security文档:Spring Security documentation : Class UnAuthenticatedServerOAuth2AuthorizedClientRepository

我试图在以下GitHub项目中总结一个演示案例:GitHub - Spring Security OAuth2 Machine-To-Machine scenario

我希望这可以为您提供有关WebClient配置的更多见解。请问您是否有任何疑问。