【问题标题】:Getting 403 Forbidden for WebFluxTest in Oauth2 Secured (Client Credentials) Resource Server Application在 Oauth2 安全(客户端凭据)资源服务器应用程序中为 WebFluxTest 获取 403 禁止
【发布时间】:2020-10-17 12:27:07
【问题描述】:

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

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

以下是我的安全配置:

@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();
  }

}

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

我的应用程序 yaml:

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");
});
    
  }
}

我的测试用例:

@RunWith(SpringRunner.class)
@WebFluxTest(controllers = {MyController.class})
@Import({WebSecurityConfiguration.class})
@WithMockUser
public class MyControllerTest {


  @Autowired
  private WebTestClient webTestClient;

  @Test
  public void test_greet() throws Exception {

    webTestClient.mutateWith(csrf()).get()
        .uri("/my/api/greet")
        .exchange()
        .expectStatus().isOk();
  }

}

注意:- 我不能通过不使用我的 WebSecurityConfiguration 类来绕过。因为在 websecurityconfiguration 中添加的过滤器中设置了响应式上下文。

【问题讨论】:

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


    【解决方案1】:

    这里需要两件事:

    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
    

    【讨论】:

      猜你喜欢
      • 2017-01-30
      • 2021-07-13
      • 2020-02-02
      • 2012-12-17
      • 1970-01-01
      • 1970-01-01
      • 2021-08-16
      • 1970-01-01
      • 2019-07-06
      相关资源
      最近更新 更多