Ultracoder
Ultracoder

Reputation: 31

How to get spring security context in reactive webflux

I have a problem with my spring boot application (version 2.6.3). I have configured reactive spring security like there:

MyApplication.java:

@SpringBootApplication
@EnableWebFlux
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class);
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(final ServerHttpSecurity http, final ReactiveOpaqueTokenIntrospector reactiveOpaqueTokenIntrospector) {
        return http.authorizeExchange()
                .anyExchange().authenticated()
                .and()
                .httpBasic().disable()
                .cors().and()
                .logout().disable()
                .formLogin().disable()
                .oauth2ResourceServer()
                .opaqueToken()
                .introspector(reactiveOpaqueTokenIntrospector)
                .and().and()
                .csrf()
                .disable()
                .build();
    }
    
}

And this is my web resource (controller):

MyWebResource.java:

@RestController
public class MyWebResource implements MyWebResourceApi {

    
    @PreAuthorize("hasRole('ROLE_USER')")
    @Override
    public Mono<String> details(String userId, ServerWebExchange exchange) {
        return exchange.getPrincipal().map(Principal::getName);
    }
}

It's work fine, when my access token is expired or incorrect the request should be denied. However when PreAuthorized allow request, my user principal will be never resolved in my exchange...

Upvotes: 2

Views: 8164

Answers (2)

lanoxx
lanoxx

Reputation: 13051

I found this answer looking for a way to add the access token to my webclient requests. If we are using OAuth2 or OpenID Connect and want to access the token instead of the principal's name, then this is not possible via the principal in the security context.

Instead we need to create a ServerOAuth2AuthorizedClientExchangeFilterFunction and register it as a filter function to the WebClient:

ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
    new ServerOAuth2AuthorizedClientExchangeFilterFunction(
        clientRegistrations,
        authorizedClients);

        oauth.setDefaultOAuth2AuthorizedClient(true);

where clientRegistrations is an injectabile bean of type ReactiveClientRegistrationRepository and authorizedClients is an injectable bean of type ServerOAuth2AuthorizedClientRepository.

We can then use the filters method for the builder to add our filter function to the exchangeFilterFunctions:

WebClient.builder()
         .filters(exchangeFilterFunctions -> {
                    exchangeFilterFunctions.add(oauth);
                  })
         .build();

Baeldung has a nice background article about this, which explains it in more detail: https://www.baeldung.com/spring-webclient-oauth2

Upvotes: 1

Alex
Alex

Reputation: 5924

In reactive application authentication information is stored in the Reactive flow and accessible from Mono/Flux. You could use ReactiveSecurityContextHolder to obtain the currently authenticated principal, or an authentication request token.

@PreAuthorize("hasRole('ROLE_USER')")
public Mono<String> details() {
        return ReactiveSecurityContextHolder.getContext()
                .map(ctx -> ((Principal) ctx.getAuthentication().getPrincipal()).getName());
    }

Upvotes: 7

Related Questions