-1

I have a method:

@GetMapping("/foo")
public void> foo(JwtAuthenticationToken token) throws ExecutionException, InterruptedException {
    Object object = ReactiveSecurityContextHolder.getContext()
            .map(securityContext -> securityContext.getAuthentication().getPrincipal())
            .toFuture()
            .get();
    System.out.println(object);

JwtAuthenticationToken object which is method argument is succesfully autowired and not null but result of

Object object = ReactiveSecurityContextHolder.getContext()
                .map(securityContext -> securityContext.getAuthentication().getPrincipal())
                .toFuture()
                .get(); 

is null.

Could you please explain why ? Is there way to fix it ?

related topic: How to get jwt token value in spring webflux? (to exchange it with Minio STS token)

gstackoverflow
  • 36,709
  • 117
  • 359
  • 710
  • Out of curiosity did you try this. `var jwtToken = ReactiveSecurityContextHolder.getContext().map(securityContext ->securityContext.getAuthentication().getPrincipal()).block()`. You can get SecurityContext in Controller by injecting it too. – nicholasnet Jan 04 '23 at 01:14
  • @nicholasnet yes, Itried it but I received the error like this: `block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-6 ` – gstackoverflow Jan 04 '23 at 08:44

2 Answers2

1

It is not necessary to use ReactiveSecurityContextHolder to get the Jwt instance. For example, if JwtAuthenticationToken is non-null, you can get the Jwt instance by doing:

public Mono<Void> foo(JwtAuthenticationToken token) throws ExecutionException, InterruptedException {
    Jwt jwt = token.getToken();
    // ...
}

Or, you can translate it with @AuthenticationPrincipal like so:

public Mono<Void> foo(@AuthenticationPrincipal Jwt jwt) throws ExecutionException, InterruptedException {
    // ...
}

Further, it is not possible to use block() with ReactiveSecurityContextHolder in the way the OP describes. block() subscribes immediately, and there is nothing immediately available in the Reactor Context at this point.

The reactive stack works somewhat in the inverse to perhaps what you are thinking. While there is a filter in Spring Security called ReactorContextWebFilter that populates the Reactor Context with the SecurityContext, its work is deferred until the HTTP response is subscribed to, for example by the browser. block() at this point states (correctly) that the Reactor Context is empty. Instead, if you participate in the existing subscription (instead of calling block()), then you are also deferring your work in the same way and will be able to use ReactiveSecurityContextHolder.

EDIT: Having read the additional context about the OP's situation from How to get jwt token value in spring webflux? (to exchange it with Minio STS token), it's clear now that the OP is aware of these ways to get the instance of a Jwt, and does not want to use them. I'll leave the answer here for completeness anyway.

jzheaux
  • 7,042
  • 3
  • 22
  • 36
  • Looks like you didn't get the issue. We have `ReactiveSecurityContextHolder` and my aim is getting the instance of `Jwt`. – gstackoverflow Jan 04 '23 at 08:47
  • Spring Webflux can resolve the `Jwt` as a method argument. Please see this sample and let me know what I'm misunderstanding: https://github.com/spring-projects/spring-security-samples/blob/main/reactive/webflux/java/oauth2/resource-server/src/main/java/example/OAuth2ResourceServerController.java – jzheaux Jan 04 '23 at 15:30
  • Also, I've edited my answer for clarity. It "[gets] the instance of `Jwt`" as I think you've described. – jzheaux Jan 04 '23 at 15:32
  • I need to get `Jwt` from `ReactiveSecurityContextHolder` and and in the execution thread. You could me ask why ? As explained here I need to create MinioClient and I want to have it single for the whole application instance. And this MinoClient accepts lambda: ` return new WebIdentityProvider(() -> new Jwt(token, 1000), "stsEndpoint", null, null, null, null, null); ` Let me know if you have any questions. – gstackoverflow Jan 05 '23 at 08:00
  • I am not sure I clearly understand what options do you mean in your **EDIT** section. Could you please clarify ? – gstackoverflow Jan 06 '23 at 18:53
  • I also found the similar question here: https://stackoverflow.com/questions/70701792/how-do-i-extract-information-from-jwt-which-is-stored-in-reactivesecuritycontext – gstackoverflow Jan 09 '23 at 11:34
  • any ideas about it? – gstackoverflow Jan 10 '23 at 07:34
  • @gstackoverflow, I don't think I'm understanding. The answer here addresses the question asked. I think the issue here is that your real question is about using MinioClient in a reactive stack, which appears to already have been answered. I really don't have more to add. The answer I gave makes sense in the context of this question alone. I think your related post about MinioClient is a better question. If you have further questions, you can often find me or another member of the team on Spring Security's gitter. Happy to chat there. – jzheaux Jan 17 '23 at 20:55
  • sorry but my topic contains ```Could you please explain why ? Is there way to fix it ?``` I don't see answer for that questions – gstackoverflow Jan 18 '23 at 09:36
  • "Is there a way to fix it" - no, nothing is broken, you cannot use `block` in this way with `ReactiveSecurityContextHolder`. "Could you please explain why?" - yes, it's because when you call `block` it subscribes immediately, but there is no Reactor Context yet to pull the `SecurityContext` from. There won't be a `SecurityContext` available in the Reactor Context until the request that kicked off the filter chain subscribes to the response, which hasn't happened at this point in the request. – jzheaux Jan 18 '23 at 18:32
0

Not really sure how token is related to the result of the bucketExists and why do you convert it to the CompletableFuture but here is an example how you can get token from the context.

@GetMapping(value = "/someEndpoint")
public Mono<Boolean> foo() {
    return ReactiveSecurityContextHolder.getContext()
            .map(ctx -> ctx.getAuthentication().getPrincipal())
            .cast(Jwt.class)
            .map(jwt -> useToken(jwt));  

}

Here is a test using org.springframework.security:spring-security-test

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
class RouteConfigTest {
    @Autowired
    private WebTestClient client;

    @Test
    void test() {
        this.client.mutateWith(mockJwt())
                .get()
                .uri("/someEndpoint")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .expectStatus().isOk();
    }
}
Alex
  • 4,987
  • 1
  • 8
  • 26
  • Sorry for confusing naming (I've fixed it). The idea that I want to get JWT inside controller (not as a result of endpoint call) using ReactiveSecurityContextHolder. And I want to get JWT (not Mono) – gstackoverflow Jan 01 '23 at 11:16
  • Just add `.map`and you will get a `Jwt` as an argument for a lambda function – Alex Jan 01 '23 at 18:29
  • I want to pass it to non-reactive context so it is not an option – gstackoverflow Jan 01 '23 at 23:33
  • Not sure what do you mean by non-reactive context but the whole idea of the reactive API and webfliux is to create non-blocking flows. Please clarify question and provide more details – Alex Jan 02 '23 at 00:02
  • details are here: https://stackoverflow.com/questions/74875058/how-to-get-jwt-token-value-in-spring-webflux-to-exchange-it-with-minio-sts-tok – gstackoverflow Jan 02 '23 at 15:11
  • Unfortunately, not all libraries support reactive API – gstackoverflow Jan 02 '23 at 15:13
  • There is nothing wrong with non-reactive code and there is a way to integrate it into reactive flow. The mentioned example is using ‘map(jwt -> MinioClient.builder()….‘ but you are keep saying that it’s not an option. It would be useful if you update the question and explain problem more clearly – Alex Jan 02 '23 at 16:17
  • as I explained there it is not an option because new MinioClient is created on each request. I expected to have single MinioClient for the whole application instance – gstackoverflow Jan 02 '23 at 16:42
  • Is it duplicate question? Don’t see any MinioClient in your question – Alex Jan 02 '23 at 17:00
  • there is no duplication but here I explained the issue without redundant details because it could be not only related to Minio – gstackoverflow Jan 03 '23 at 22:23
  • non-reactive APIs can be called by reactive APIs as already described in Alex's comment about using `.map`. I believe you should open a new question and ask how to invoke MinioClient from a reactive context. Given the detail you've offered thus far, it still seems like an option, so a separate question would allow you to add the missing context we all need in order to help. – jzheaux Jan 03 '23 at 22:38
  • Basically you have 2 options - get token from reactive context and use in a ‘.map‘ or get token in your MinioClient by using ‘.block()‘ but it will be blocking and you would need to schedule it on ‘boundedElastic‘. an alternative idea could be to use AWS Java SDK v2 that is fully async and could be wrapped by reactive API unless you need something minio specific. – Alex Jan 04 '23 at 03:35
  • @Alex I would strongly appreciate if you could provide the whole answer for both options – gstackoverflow Jan 04 '23 at 08:45
  • @jzheaux All details are here: https://stackoverflow.com/questions/74875058/how-to-get-jwt-token-value-in-spring-webflux-to-exchange-it-with-minio-sts-tok – gstackoverflow Jan 04 '23 at 08:53
  • @Alex could you please provide details ? – gstackoverflow Jan 09 '23 at 10:08
  • I also found the similar question here: https://stackoverflow.com/questions/70701792/how-do-i-extract-information-from-jwt-which-is-stored-in-reactivesecuritycontext – gstackoverflow Jan 09 '23 at 11:34
  • Not really sure what else do you need here. "It returns a Mono but I need String" is very common question and as many people mentioned here, the only way to get it - build reactive flow and get it as a part of the flow. Do you really need webflux and reactive if you are blocking? Async programming is not easy but you will loose all advantages of the reactive if you start blocking. – Alex Jan 11 '23 at 16:53