|
@@ -23,6 +23,7 @@ import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
import org.springframework.util.Assert;
|
|
import org.springframework.util.Assert;
|
|
import reactor.core.publisher.Mono;
|
|
import reactor.core.publisher.Mono;
|
|
|
|
+import reactor.core.scheduler.Scheduler;
|
|
import reactor.core.scheduler.Schedulers;
|
|
import reactor.core.scheduler.Schedulers;
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -37,6 +38,8 @@ public class UserDetailsRepositoryReactiveAuthenticationManager implements React
|
|
|
|
|
|
private PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
|
private PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
|
|
|
|
|
|
|
+ private Scheduler scheduler = Schedulers.parallel();
|
|
|
|
+
|
|
public UserDetailsRepositoryReactiveAuthenticationManager(ReactiveUserDetailsService userDetailsService) {
|
|
public UserDetailsRepositoryReactiveAuthenticationManager(ReactiveUserDetailsService userDetailsService) {
|
|
Assert.notNull(userDetailsService, "userDetailsService cannot be null");
|
|
Assert.notNull(userDetailsService, "userDetailsService cannot be null");
|
|
this.userDetailsService = userDetailsService;
|
|
this.userDetailsService = userDetailsService;
|
|
@@ -46,7 +49,7 @@ public class UserDetailsRepositoryReactiveAuthenticationManager implements React
|
|
public Mono<Authentication> authenticate(Authentication authentication) {
|
|
public Mono<Authentication> authenticate(Authentication authentication) {
|
|
final String username = authentication.getName();
|
|
final String username = authentication.getName();
|
|
return this.userDetailsService.findByUsername(username)
|
|
return this.userDetailsService.findByUsername(username)
|
|
- .publishOn(Schedulers.parallel())
|
|
|
|
|
|
+ .publishOn(this.scheduler)
|
|
.filter( u -> this.passwordEncoder.matches((String) authentication.getCredentials(), u.getPassword()))
|
|
.filter( u -> this.passwordEncoder.matches((String) authentication.getCredentials(), u.getPassword()))
|
|
.switchIfEmpty(Mono.defer(() -> Mono.error(new BadCredentialsException("Invalid Credentials"))))
|
|
.switchIfEmpty(Mono.defer(() -> Mono.error(new BadCredentialsException("Invalid Credentials"))))
|
|
.map( u -> new UsernamePasswordAuthenticationToken(u, u.getPassword(), u.getAuthorities()) );
|
|
.map( u -> new UsernamePasswordAuthenticationToken(u, u.getPassword(), u.getAuthorities()) );
|
|
@@ -61,4 +64,20 @@ public class UserDetailsRepositoryReactiveAuthenticationManager implements React
|
|
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
|
|
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
|
|
this.passwordEncoder = passwordEncoder;
|
|
this.passwordEncoder = passwordEncoder;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Sets the {@link Scheduler} used by the {@link UserDetailsRepositoryReactiveAuthenticationManager}.
|
|
|
|
+ * The default is {@code Schedulers.parallel()} because modern password encoding is
|
|
|
|
+ * a CPU intensive task that is non blocking. This means validation is bounded by the
|
|
|
|
+ * number of CPUs. Some applications may want to customize the {@link Scheduler}. For
|
|
|
|
+ * example, if users are stuck using the insecure {@link org.springframework.security.crypto.password.NoOpPasswordEncoder}
|
|
|
|
+ * they might want to leverage {@code Schedulers.immediate()}.
|
|
|
|
+ *
|
|
|
|
+ * @param scheduler the {@link Scheduler} to use. Cannot be null.
|
|
|
|
+ * @since 5.0.6
|
|
|
|
+ */
|
|
|
|
+ public void setScheduler(Scheduler scheduler) {
|
|
|
|
+ Assert.notNull(scheduler, "scheduler cannot be null");
|
|
|
|
+ this.scheduler = scheduler;
|
|
|
|
+ }
|
|
}
|
|
}
|