在Oauth2安全(客户端凭据)资源服务器应用程序中为WebFluxTest禁止403

时间:2020-06-26 20:44:32

标签: java spring-security-oauth2 spring-webflux spring-security-test

我有一个反应式(Spring WebFlux)Web应用程序,其中我只有很少的受保护资源的REST API。(Oauth2)。要手动访问它们,我需要获取具有客户端凭据授予类型的授权令牌,并在请求中使用该令牌。

现在,我需要编写测试,以通过Spring的WebTestClient进行调用来调用API。我在尝试访问API时被禁止403。编写测试用例时我在哪里做错了。

以下是我的安全配置:

range()

注意:-我需要这个webclient bean,因为在该过滤器(我添加到SecurityWebFilterChain中)中,我正在调用另一个受保护的资源/ API,并且该API的响应是在响应上下文中设置的

我的应用程序Yaml:

@EnableWebFluxSecurity
public class WebSecurityConfiguration {

  @Bean
  SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeExchange()
        .pathMatchers(ACTUATOR_ENDPOINT_PATTERN)
        .permitAll()
        .pathMatchers("/my/api/*")
        .hasAuthority("SCOPE_myApi")
        .anyExchange().authenticated()
        .and()
        .oauth2ResourceServer()
        .jwt();
    http.addFilterAfter(new SomeFilter(), SecurityWebFiltersOrder.AUTHORIZATION);

    return http.build();
  }

  @Bean
  public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
      ReactiveClientRegistrationRepository clientRegistrationRepository,
      ReactiveOAuth2AuthorizedClientService authorizedClientService) {

    ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
        ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
            .clientCredentials()
            .build();

    AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
        new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientService);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

    return authorizedClientManager;
  }

  @Bean
  public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
    ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
    return WebClient.builder().filter(oauth).build();
  }

}

我的控制器:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: ${oidc-issuer-uri}
      client:
        provider:
          myProvider:
            issuer-uri: ${oidc-issuer-uri}
        registration:
          myProvider:
            client-id: another-service-client
            client-secret: ${another-service-clientSecret}
            scope: anotherServiceScope
            authorization-grant-type: client_credentials

我的测试用例:

@RestController
public class MyController {
 @GetMapping(value = "/my/api/greet")
  public Mono<String> greet() {
return Mono.subscriberContext()
        .flatMap(context -> {
String someVal = context.get("MY_CONTEXT"); //This context is being set inside the filter 'SomeFilter'
//Use this someVal
return Mono.just("Hello World");
});
    
  }
}

注意:-我无法通过不使用WebSecurityConfiguration类来绕过。因为反应性上下文是在添加到websecurity配置中的过滤器中设置的。

1 个答案:

答案 0 :(得分:0)

这里需要两件事:

  1. 首先要访问/ my / api / greet,webTestClient需要SCOPE_myApi,并且由于这里没有“用户”,所以我们不需要@WithMockUser
  @Test
  public void test_greet() {
    
    webTestClient
        .mutateWith(mockOidcLogin().authorities(new SimpleGrantedAuthority("SCOPE_myApi")))
        .get()
        .uri("/my/api/greet")
        .exchange()
        .expectStatus().isOk()
        .expectBody(String.class).isEqualTo("mockSasToken");
  }
  1. 接下来,我们需要一个Wiremock服务器来模拟“另一个服务”的响应

为此,一个选择是使用spring boot @AutoConfigureWireMock(port = 0)自动启动一个Wiremock服务器,并在一个随机端口为我们关闭。

接下来,我们在测试方法中对“另一项服务”和Oauth2令牌端点的响应进行存根。

最后,我们需要一个“测试” spring配置文件和一个对应的application-test.yaml,在其中我们告诉Spring使用Wiremock端点来获取令牌:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: http://localhost:${wiremock.server.port}/.well-known/jwks_uri
      client:
        provider:
          myProvider:
            token-uri: http://localhost:${wiremock.server.port}/.well-known/token
        registration:
          myProvider:
            client-id: mockClient
            client-secret: mockSecret