J'ai une application web réactive (Spring WebFlux) où j'ai quelques API REST qui sont des ressources protégées (Oauth2). Pour y accéder manuellement, j'ai besoin d'obtenir un jeton d'autorisation avec les informations d'identification du client et d'utiliser ce jeton dans la requête.
Je dois maintenant écrire des tests dans lesquels je peux invoquer les API en passant par le WebTestClient de Spring. J'obtiens 403 forbidden en essayant d'accéder à l'API. Où est-ce que je me trompe en écrivant le cas de test ?
Voici ma configuration de sécurité :
@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();
}
}
Note:- J'ai besoin de ce bean webclient parce qu'à l'intérieur de ce filtre (que j'ai ajouté à la SecurityWebFilterChain), j'appelle une autre ressource/API protégée et la réponse de cette API est définie dans le contexte réactif.
Mon application 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
Mon contrôleur :
@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");
});
}
}
Mon cas de test :
@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();
}
}
Note:- Je ne peux pas contourner le problème en n'utilisant pas ma classe WebSecurityConfiguration. Parce que le contexte réactif est défini dans le filtre qui est ajouté dans la websecurityconfiguration.