Summary
Documentation here and here shows example of how to get Authentication token using ReactiveSecurityContextHolder.getContext() but this is misleading because it always returns null!
I spent days on trying to figure out what I'm doing wrong in my AuthenticationManager, ServerSecurityContextRepository and/or WebFilters to get current logged user in my Controller:
@GetMapping
@PreAuthorize("hasRole('USER')")
Mono<String> getMyProfile() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.map(Authentication::getName);
}
and finally I found this issue: https://github.com/spring-projects/spring-security/issues/4790, changed my Controller to:
@GetMapping
@PreAuthorize("hasRole('USER')")
Mono<String> getMyProfile(@AuthenticationPrincipal Mono<UserDetails> details) {
return details
.cast(User.class)
.map(User::getLogin);
}
and it magically worked (still I have no idea why....)
Actual Behavior
Documentation is not clear on how to obtain UserDetails/Principle in Controller
Expected Behavior
Documentation should explain exactly what is written in this comment: https://github.com/spring-projects/spring-security/issues/4790#issuecomment-342168566
Comment From: eximius313
It occurred, that this was not a problem with documentation, but actually a bug!
I was blocking on Context:
final SecurityContext context = ReactiveSecurityContextHolder.getContext().block(); and the context was always null.
Because I was using custom AuthenticationToken, in tests I used: SecurityMockServerConfigurers.mockUser; and it also was returning null.
Surprisingly, when I tried WithMockUser instead - it worked!
1) Run the sample: SpringSecurityReactiveSample.zip with ./gradlew test and you'll get NPE.
2) Go to ControllerTest, comment the line 65 and uncomment 62
2) Run ./gradlew test again, and tests pass
Comment From: yyunikov
+1 Same for me, I always get a null in ReactiveSecurityContextHolder for some reason.
Comment From: LBoraz
Still a bug in 5.2.2, ReactiveSecurityContextHolder returns NEVER a context, not even when mocked in unit tests
Comment From: luvarqpp
I have an issue, which pointed me here. I am trying to use CsrfMutator and it fails on NPE. Can this issue be connected to https://github.com/spring-projects/spring-security/issues/7778 ?
Comment From: rwinch
You cannot use .block() because it subscribes and does not have the security context as part of the subscriber context that is established by Spring Security's WebFilter.
If you believe this is a bug, please provide a complete and minimal sample (i.e. GitHub repository) of the problem with details on how to reproduce the issue, what you expect to happen, and what actually happens.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: eximius313
@rwinch - I have provided the sample project in my second comment two years ago (https://github.com/spring-projects/spring-security/issues/5207#issuecomment-379277990)
Comment From: rwinch
Thanks for the pointer. Sorry I wasn't clear. I wasn't sure if someone had an example that does not use .block(). Some comments seem to imply that ReactiveSecurityContextHolder never works for them.
ReactiveSecurityContextHolder uses Reactor's Context and does not allow .block() because the subscriber context won't be propagated. For understanding how Reactor's Context works refer to Adding a Context to a Reactive Sequence section of their reference.
More generally, you should not invoke .block() in async threads because that breaks reactive semantics (which should not be blocking). In newer versions of Reactor it will throw an Exception if you try to .block() any Publisher in an async thread.
I'm going to close this issue as invalid since the example uses .block(). If someone has an issue leveraging ReactiveSecurityContextHolder without using .block() then please create a new ticket.
Comment From: Nida96
Still not fixed without using block
Comment From: sohelnaikawadi1
I can confirm it still doesn't work without using block().