Summary
I recently updated a project from a pre-release of spring boot 2 to the most current 2.0.5 build. I am no longer able to retrieve the username of the currently authenticated user for use in calls.
I have tried both via the Principal in the ServerRequest, and the Authentication in the ReactiveSecurityContext.
Authentication itself is successful, with the automatically build in login form and my MapReactiveUserDetailsService.
Actual Behavior
User name is empty
Expected Behavior
User name matches that of authenticated user.
Configuration
I am using Method Security.
@Bean
fun securityWebFilterChain(
http: ServerHttpSecurity): SecurityWebFilterChain {
return http
.authorizeExchange()
.anyExchange().authenticated()
.and()
.formLogin()
.and()
.build()
}
@Bean
fun userDetailsService(
encoder: BCryptPasswordEncoder,
userRepository: UserRepository,
environment: Environment): MapReactiveUserDetailsService {
var users = userRepository.findAll().map {
User.withUsername(it.username)
.password(it.password)
.roles("USER")
.build()
}.toIterable().toList()
if(users.isEmpty()) {
val adminPassword = environment.getProperty("news.admin.password", String::class.java)
if(adminPassword != null) {
val admin = User.withUsername("Admin")
.password(encoder.encode(adminPassword))
.roles("USER", "ADMIN")
.build()
users += admin
}
}
return MapReactiveUserDetailsService(users)
}
Version
Most current version included in Spring Boot 2.0.5.RELEASE
Sample
@PreAuthorize("isAuthenticated()")
fun getUserNews(serverRequest: ServerRequest): Flux<ViewModel> {
// val username = ReactiveSecurityContextHolder.getContext().map { it.authentication }.map { it.name }
// .switchIfEmpty(Mono.error(IllegalStateException("Unable to retrieve username")))
val username = serverRequest.principal().map { it.name }
.switchIfEmpty(Mono.error(IllegalStateException("Unable to retrieve username")))
val user = userRepo.findByUsername(username)
return articleRepo
.findAll()
.zipWith(user.repeat())
.filter { tup -> !tup.t2.ignored.contains(tup.t1.id)}
.sort { a1, a2 -> a1.t1.time.compareTo(a2.t1.time) }
.map { it.t1.toViewModel() }
}
Comment From: rwinch
Thanks for the report. I don't see an issue looking at your code, nor am I able to reproduce the issue. Can you provide a complete sample that reproduces this?
Comment From: dillius
Let's make this even weirder:
If I switch the reactor .map { } for .flatMap { }, it works.
I would have to guess this is some quality of the reactor processing; this fix was discussed as having something to do with the availability of the principal being dependent on a delegate.