After about 2 weeks of reading countless tutorials/javadocs and trying to get Spring security to work using a webflux, R2DBC and mysql combo to work, I'm finally ready to admit that I'm stuck :(
Every login request is being blocked, even though the details are correct (matched using online BCrypt verifier).
Is there a gap in my understanding? have I missed something?
Any pointers would be greatly appreciated.
ReactiveAuthenticationManager
@Bean
protected ReactiveAuthenticationManager reactiveAuthenticationManager() {
log.info("Received authentication request");
return authentication -> {
UserDetailsRepositoryReactiveAuthenticationManager authenticator = new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
authenticator.setPasswordEncoder(passwordEncoder);
return authenticator.authenticate(authentication);
};
}
UserDetailsService
@Component
public class UserDetailsService implements ReactiveUserDetailsService {
@Autowired
public UserRepository userRepository;
@Override
public Mono<UserDetails> findByUsername(String username) {
return userRepository.findByUsername(username).map(CustomUser::new);
};
}
Filters
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
//require that all
.authorizeExchange(
exchanges ->exchanges.anyExchange().authenticated()
)
.httpBasic(withDefaults())
//this allows js to read cookie
.csrf(csrf -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))
.formLogin(
withDefaults()
);
return http.build();
}
CustomUser
public class CustomUser implements UserDetails {
private String username;
private String password;
private int enabled;
public CustomUser(){
}
public CustomUser(UserDetails user){
this.setUsername(user.getUsername());
this.setPassword(user.getPassword());
this.setEnabled(user.isEnabled() == true?1:0);
log.info("Match found : " + this.toString());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return this.enabled == 1;
}
}
Answering my own question, just in case anybody is having the same problem.
AuthenticationManager
interface and theAuthenticationProvider
interface both have theauthenticate()
method. I belive the correct one to use would be one from class<? extends AuthenticationProvider>
However, in the absence of a ready-made reactive
AuthenticationProvider
for databases, I simply did the following:Hope this helps somebody.